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.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +3 -0
- data/.travis.yml +24 -0
- data/CHANGELOG.md +2006 -0
- data/CODE_OF_CONDUCT.md +40 -0
- data/CONTRIBUTING.md +23 -0
- data/DEVELOPMENT.md +119 -0
- data/ISSUES.md +96 -0
- data/LICENSE.md +23 -0
- data/README.md +32 -0
- data/Rakefile +308 -0
- data/bin/carat +21 -0
- data/bin/carat_ruby +56 -0
- data/carat.gemspec +32 -0
- data/lib/carat.rb +446 -0
- data/lib/carat/anonymizable_uri.rb +32 -0
- data/lib/carat/capistrano.rb +16 -0
- data/lib/carat/cli.rb +407 -0
- data/lib/carat/cli/binstubs.rb +38 -0
- data/lib/carat/cli/cache.rb +35 -0
- data/lib/carat/cli/check.rb +35 -0
- data/lib/carat/cli/clean.rb +26 -0
- data/lib/carat/cli/common.rb +56 -0
- data/lib/carat/cli/config.rb +84 -0
- data/lib/carat/cli/console.rb +38 -0
- data/lib/carat/cli/exec.rb +44 -0
- data/lib/carat/cli/gem.rb +195 -0
- data/lib/carat/cli/init.rb +33 -0
- data/lib/carat/cli/inject.rb +33 -0
- data/lib/carat/cli/install.rb +156 -0
- data/lib/carat/cli/open.rb +23 -0
- data/lib/carat/cli/outdated.rb +80 -0
- data/lib/carat/cli/package.rb +45 -0
- data/lib/carat/cli/platform.rb +43 -0
- data/lib/carat/cli/show.rb +74 -0
- data/lib/carat/cli/update.rb +73 -0
- data/lib/carat/cli/viz.rb +27 -0
- data/lib/carat/constants.rb +5 -0
- data/lib/carat/current_ruby.rb +183 -0
- data/lib/carat/definition.rb +628 -0
- data/lib/carat/dep_proxy.rb +43 -0
- data/lib/carat/dependency.rb +110 -0
- data/lib/carat/deployment.rb +59 -0
- data/lib/carat/deprecate.rb +15 -0
- data/lib/carat/dsl.rb +331 -0
- data/lib/carat/endpoint_specification.rb +76 -0
- data/lib/carat/env.rb +75 -0
- data/lib/carat/environment.rb +42 -0
- data/lib/carat/fetcher.rb +423 -0
- data/lib/carat/friendly_errors.rb +85 -0
- data/lib/carat/gem_helper.rb +180 -0
- data/lib/carat/gem_helpers.rb +26 -0
- data/lib/carat/gem_installer.rb +9 -0
- data/lib/carat/gem_path_manipulation.rb +8 -0
- data/lib/carat/gem_tasks.rb +2 -0
- data/lib/carat/graph.rb +169 -0
- data/lib/carat/index.rb +197 -0
- data/lib/carat/injector.rb +64 -0
- data/lib/carat/installer.rb +339 -0
- data/lib/carat/lazy_specification.rb +83 -0
- data/lib/carat/lockfile_parser.rb +167 -0
- data/lib/carat/match_platform.rb +13 -0
- data/lib/carat/psyched_yaml.rb +26 -0
- data/lib/carat/remote_specification.rb +57 -0
- data/lib/carat/resolver.rb +334 -0
- data/lib/carat/retry.rb +60 -0
- data/lib/carat/ruby_dsl.rb +11 -0
- data/lib/carat/ruby_version.rb +117 -0
- data/lib/carat/rubygems_ext.rb +170 -0
- data/lib/carat/rubygems_integration.rb +619 -0
- data/lib/carat/runtime.rb +289 -0
- data/lib/carat/settings.rb +208 -0
- data/lib/carat/setup.rb +24 -0
- data/lib/carat/shared_helpers.rb +149 -0
- data/lib/carat/similarity_detector.rb +63 -0
- data/lib/carat/source.rb +46 -0
- data/lib/carat/source/git.rb +294 -0
- data/lib/carat/source/git/git_proxy.rb +162 -0
- data/lib/carat/source/path.rb +226 -0
- data/lib/carat/source/path/installer.rb +43 -0
- data/lib/carat/source/rubygems.rb +381 -0
- data/lib/carat/source_list.rb +101 -0
- data/lib/carat/spec_set.rb +154 -0
- data/lib/carat/ssl_certs/.document +1 -0
- data/lib/carat/ssl_certs/AddTrustExternalCARoot-2048.pem +25 -0
- data/lib/carat/ssl_certs/AddTrustExternalCARoot.pem +32 -0
- data/lib/carat/ssl_certs/Class3PublicPrimaryCertificationAuthority.pem +14 -0
- data/lib/carat/ssl_certs/DigiCertHighAssuranceEVRootCA.pem +23 -0
- data/lib/carat/ssl_certs/EntrustnetSecureServerCertificationAuthority.pem +28 -0
- data/lib/carat/ssl_certs/GeoTrustGlobalCA.pem +20 -0
- data/lib/carat/ssl_certs/certificate_manager.rb +66 -0
- data/lib/carat/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem +21 -0
- data/lib/carat/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem +23 -0
- data/lib/carat/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem +25 -0
- data/lib/carat/templates/Executable +16 -0
- data/lib/carat/templates/Executable.standalone +12 -0
- data/lib/carat/templates/Gemfile +4 -0
- data/lib/carat/templates/newgem/.travis.yml.tt +3 -0
- data/lib/carat/templates/newgem/CODE_OF_CONDUCT.md.tt +13 -0
- data/lib/carat/templates/newgem/Gemfile.tt +4 -0
- data/lib/carat/templates/newgem/LICENSE.txt.tt +21 -0
- data/lib/carat/templates/newgem/README.md.tt +39 -0
- data/lib/carat/templates/newgem/Rakefile.tt +25 -0
- data/lib/carat/templates/newgem/bin/console.tt +14 -0
- data/lib/carat/templates/newgem/bin/setup.tt +7 -0
- data/lib/carat/templates/newgem/exe/newgem.tt +3 -0
- data/lib/carat/templates/newgem/ext/newgem/extconf.rb.tt +3 -0
- data/lib/carat/templates/newgem/ext/newgem/newgem.c.tt +9 -0
- data/lib/carat/templates/newgem/ext/newgem/newgem.h.tt +6 -0
- data/lib/carat/templates/newgem/gitignore.tt +16 -0
- data/lib/carat/templates/newgem/lib/newgem.rb.tt +12 -0
- data/lib/carat/templates/newgem/lib/newgem/version.rb.tt +7 -0
- data/lib/carat/templates/newgem/newgem.gemspec.tt +43 -0
- data/lib/carat/templates/newgem/rspec.tt +2 -0
- data/lib/carat/templates/newgem/spec/newgem_spec.rb.tt +11 -0
- data/lib/carat/templates/newgem/spec/spec_helper.rb.tt +2 -0
- data/lib/carat/templates/newgem/test/minitest_helper.rb.tt +4 -0
- data/lib/carat/templates/newgem/test/test_newgem.rb.tt +11 -0
- data/lib/carat/ui.rb +7 -0
- data/lib/carat/ui/rg_proxy.rb +21 -0
- data/lib/carat/ui/shell.rb +103 -0
- data/lib/carat/ui/silent.rb +44 -0
- data/lib/carat/vendor/molinillo/lib/molinillo.rb +5 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/dependency_graph.rb +266 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/errors.rb +69 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/gem_metadata.rb +3 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +90 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/modules/ui.rb +63 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/resolution.rb +415 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/resolver.rb +43 -0
- data/lib/carat/vendor/molinillo/lib/molinillo/state.rb +43 -0
- data/lib/carat/vendor/net/http/faster.rb +26 -0
- data/lib/carat/vendor/net/http/persistent.rb +1230 -0
- data/lib/carat/vendor/net/http/persistent/ssl_reuse.rb +128 -0
- data/lib/carat/vendor/thor/lib/thor.rb +484 -0
- data/lib/carat/vendor/thor/lib/thor/actions.rb +319 -0
- data/lib/carat/vendor/thor/lib/thor/actions/create_file.rb +103 -0
- data/lib/carat/vendor/thor/lib/thor/actions/create_link.rb +59 -0
- data/lib/carat/vendor/thor/lib/thor/actions/directory.rb +118 -0
- data/lib/carat/vendor/thor/lib/thor/actions/empty_directory.rb +135 -0
- data/lib/carat/vendor/thor/lib/thor/actions/file_manipulation.rb +316 -0
- data/lib/carat/vendor/thor/lib/thor/actions/inject_into_file.rb +107 -0
- data/lib/carat/vendor/thor/lib/thor/base.rb +656 -0
- data/lib/carat/vendor/thor/lib/thor/command.rb +133 -0
- data/lib/carat/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +77 -0
- data/lib/carat/vendor/thor/lib/thor/core_ext/io_binary_read.rb +10 -0
- data/lib/carat/vendor/thor/lib/thor/core_ext/ordered_hash.rb +98 -0
- data/lib/carat/vendor/thor/lib/thor/error.rb +32 -0
- data/lib/carat/vendor/thor/lib/thor/group.rb +281 -0
- data/lib/carat/vendor/thor/lib/thor/invocation.rb +178 -0
- data/lib/carat/vendor/thor/lib/thor/line_editor.rb +17 -0
- data/lib/carat/vendor/thor/lib/thor/line_editor/basic.rb +35 -0
- data/lib/carat/vendor/thor/lib/thor/line_editor/readline.rb +88 -0
- data/lib/carat/vendor/thor/lib/thor/parser.rb +4 -0
- data/lib/carat/vendor/thor/lib/thor/parser/argument.rb +73 -0
- data/lib/carat/vendor/thor/lib/thor/parser/arguments.rb +175 -0
- data/lib/carat/vendor/thor/lib/thor/parser/option.rb +125 -0
- data/lib/carat/vendor/thor/lib/thor/parser/options.rb +218 -0
- data/lib/carat/vendor/thor/lib/thor/rake_compat.rb +71 -0
- data/lib/carat/vendor/thor/lib/thor/runner.rb +322 -0
- data/lib/carat/vendor/thor/lib/thor/shell.rb +81 -0
- data/lib/carat/vendor/thor/lib/thor/shell/basic.rb +421 -0
- data/lib/carat/vendor/thor/lib/thor/shell/color.rb +149 -0
- data/lib/carat/vendor/thor/lib/thor/shell/html.rb +126 -0
- data/lib/carat/vendor/thor/lib/thor/util.rb +267 -0
- data/lib/carat/vendor/thor/lib/thor/version.rb +3 -0
- data/lib/carat/vendored_fileutils.rb +9 -0
- data/lib/carat/vendored_molinillo.rb +2 -0
- data/lib/carat/vendored_persistent.rb +11 -0
- data/lib/carat/vendored_thor.rb +3 -0
- data/lib/carat/version.rb +6 -0
- data/lib/carat/vlad.rb +11 -0
- data/lib/carat/worker.rb +73 -0
- data/man/carat-config.ronn +178 -0
- data/man/carat-exec.ronn +136 -0
- data/man/carat-install.ronn +383 -0
- data/man/carat-package.ronn +66 -0
- data/man/carat-platform.ronn +42 -0
- data/man/carat-update.ronn +188 -0
- data/man/carat.ronn +98 -0
- data/man/gemfile.5.ronn +473 -0
- data/man/index.txt +7 -0
- metadata +321 -0
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
module Carat
|
|
2
|
+
class Source
|
|
3
|
+
class Git < Path
|
|
4
|
+
|
|
5
|
+
class GitNotInstalledError < GitError
|
|
6
|
+
def initialize
|
|
7
|
+
msg = "You need to install git to be able to use gems from git repositories. "
|
|
8
|
+
msg << "For help installing git, please refer to GitHub's tutorial at https://help.github.com/articles/set-up-git"
|
|
9
|
+
super msg
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class GitNotAllowedError < GitError
|
|
14
|
+
def initialize(command)
|
|
15
|
+
msg = "Carat is trying to run a `git #{command}` at runtime. You probably need to run `carat install`. However, "
|
|
16
|
+
msg << "this error message could probably be more useful. Please submit a ticket at http://github.com/caratrb/carat/issues "
|
|
17
|
+
msg << "with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}"
|
|
18
|
+
super msg
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class GitCommandError < GitError
|
|
23
|
+
def initialize(command, path = nil)
|
|
24
|
+
msg = "Git error: command `git #{command}` in directory #{Dir.pwd} has failed."
|
|
25
|
+
msg << "\nIf this error persists you could try removing the cache directory '#{path}'" if path && path.exist?
|
|
26
|
+
super msg
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# The GitProxy is responsible to interact with git repositories.
|
|
31
|
+
# All actions required by the Git source is encapsulated in this
|
|
32
|
+
# object.
|
|
33
|
+
class GitProxy
|
|
34
|
+
attr_accessor :path, :uri, :ref
|
|
35
|
+
attr_writer :revision
|
|
36
|
+
|
|
37
|
+
def initialize(path, uri, ref, revision = nil, git = nil)
|
|
38
|
+
@path = path
|
|
39
|
+
@uri = uri
|
|
40
|
+
@ref = ref
|
|
41
|
+
@revision = revision
|
|
42
|
+
@git = git
|
|
43
|
+
raise GitNotInstalledError.new if allow? && !Carat.git_present?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def revision
|
|
47
|
+
@revision ||= allowed_in_path { git("rev-parse #{ref}").strip }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def branch
|
|
51
|
+
@branch ||= allowed_in_path do
|
|
52
|
+
git("branch") =~ /^\* (.*)$/ && $1.strip
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def contains?(commit)
|
|
57
|
+
allowed_in_path do
|
|
58
|
+
result = git_null("branch --contains #{commit}")
|
|
59
|
+
$? == 0 && result =~ /^\* (.*)$/
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def version
|
|
64
|
+
git("--version").sub("git version", "").strip
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def checkout
|
|
68
|
+
if path.exist?
|
|
69
|
+
return if has_revision_cached?
|
|
70
|
+
Carat.ui.confirm "Updating #{uri}"
|
|
71
|
+
in_path do
|
|
72
|
+
git_retry %|fetch --force --quiet --tags #{uri_escaped} "refs/heads/*:refs/heads/*"|
|
|
73
|
+
end
|
|
74
|
+
else
|
|
75
|
+
Carat.ui.info "Fetching #{uri}"
|
|
76
|
+
FileUtils.mkdir_p(path.dirname)
|
|
77
|
+
git_retry %|clone #{uri_escaped} "#{path}" --bare --no-hardlinks --quiet|
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def copy_to(destination, submodules=false)
|
|
82
|
+
unless File.exist?(destination.join(".git"))
|
|
83
|
+
FileUtils.mkdir_p(destination.dirname)
|
|
84
|
+
FileUtils.rm_rf(destination)
|
|
85
|
+
git_retry %|clone --no-checkout --quiet "#{path}" "#{destination}"|
|
|
86
|
+
File.chmod((0777 & ~File.umask), destination)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
SharedHelpers.chdir(destination) do
|
|
90
|
+
git_retry %|fetch --force --quiet --tags "#{path}"|
|
|
91
|
+
git "reset --hard #{@revision}"
|
|
92
|
+
|
|
93
|
+
if submodules
|
|
94
|
+
git_retry "submodule update --init --recursive"
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
# TODO: Do not rely on /dev/null.
|
|
102
|
+
# Given that open3 is not cross platform until Ruby 1.9.3,
|
|
103
|
+
# the best solution is to pipe to /dev/null if it exists.
|
|
104
|
+
# If it doesn't, everything will work fine, but the user
|
|
105
|
+
# will get the $stderr messages as well.
|
|
106
|
+
def git_null(command)
|
|
107
|
+
git("#{command} 2>#{Carat::NULL}", false)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def git_retry(command)
|
|
111
|
+
Carat::Retry.new("git #{command}", GitNotAllowedError).attempts do
|
|
112
|
+
git(command)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def git(command, check_errors=true)
|
|
117
|
+
raise GitNotAllowedError.new(command) unless allow?
|
|
118
|
+
out = SharedHelpers.with_clean_git_env { %x{git #{command}} }
|
|
119
|
+
raise GitCommandError.new(command, path) if check_errors && !$?.success?
|
|
120
|
+
out
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def has_revision_cached?
|
|
124
|
+
return unless @revision
|
|
125
|
+
in_path { git("cat-file -e #{@revision}") }
|
|
126
|
+
true
|
|
127
|
+
rescue GitError
|
|
128
|
+
false
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Escape the URI for git commands
|
|
132
|
+
def uri_escaped
|
|
133
|
+
if Carat::WINDOWS
|
|
134
|
+
# Windows quoting requires double quotes only, with double quotes
|
|
135
|
+
# inside the string escaped by being doubled.
|
|
136
|
+
'"' + uri.gsub('"') {|s| '""'} + '"'
|
|
137
|
+
else
|
|
138
|
+
# Bash requires single quoted strings, with the single quotes escaped
|
|
139
|
+
# by ending the string, escaping the quote, and restarting the string.
|
|
140
|
+
"'" + uri.gsub("'") {|s| "'\\''"} + "'"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def allow?
|
|
145
|
+
@git ? @git.allow_git_ops? : true
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def in_path(&blk)
|
|
149
|
+
checkout unless path.exist?
|
|
150
|
+
SharedHelpers.chdir(path, &blk)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def allowed_in_path
|
|
154
|
+
return in_path { yield } if allow?
|
|
155
|
+
raise GitError, "The git source #{uri} is not yet checked out. Please run `carat install` before trying to start your application"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
module Carat
|
|
2
|
+
class Source
|
|
3
|
+
|
|
4
|
+
class Path < Source
|
|
5
|
+
autoload :Installer, 'carat/source/path/installer'
|
|
6
|
+
|
|
7
|
+
attr_reader :path, :options
|
|
8
|
+
attr_writer :name
|
|
9
|
+
attr_accessor :version
|
|
10
|
+
|
|
11
|
+
DEFAULT_GLOB = "{,*,*/*}.gemspec"
|
|
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
|
+
if options["path"]
|
|
21
|
+
@path = Pathname.new(options["path"])
|
|
22
|
+
@path = expand(@path) unless @path.relative?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@name = options["name"]
|
|
26
|
+
@version = options["version"]
|
|
27
|
+
|
|
28
|
+
# Stores the original path. If at any point we move to the
|
|
29
|
+
# cached directory, we still have the original path to copy from.
|
|
30
|
+
@original_path = @path
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def remote!
|
|
34
|
+
@allow_remote = true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def cached!
|
|
38
|
+
@allow_cached = true
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.from_lock(options)
|
|
42
|
+
new(options.merge("path" => options.delete("remote")))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def to_lock
|
|
46
|
+
out = "PATH\n"
|
|
47
|
+
out << " remote: #{relative_path}\n"
|
|
48
|
+
out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
|
|
49
|
+
out << " specs:\n"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def to_s
|
|
53
|
+
"source at #{@path}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def hash
|
|
57
|
+
[self.class, expanded_path, version].hash
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def eql?(o)
|
|
61
|
+
o.instance_of?(Path) &&
|
|
62
|
+
expanded_path == expand(o.path) &&
|
|
63
|
+
version == o.version
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
alias == eql?
|
|
67
|
+
|
|
68
|
+
def name
|
|
69
|
+
File.basename(expanded_path.to_s)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def install(spec)
|
|
73
|
+
generate_bin(spec, :disable_extensions)
|
|
74
|
+
["Using #{version_message(spec)} from #{to_s}", nil]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def cache(spec, custom_path = nil)
|
|
78
|
+
app_cache_path = app_cache_path(custom_path)
|
|
79
|
+
return unless Carat.settings[:cache_all]
|
|
80
|
+
return if expand(@original_path).to_s.index(Carat.root.to_s) == 0
|
|
81
|
+
|
|
82
|
+
unless @original_path.exist?
|
|
83
|
+
raise GemNotFound, "Can't cache gem #{version_message(spec)} because #{to_s} is missing!"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
FileUtils.rm_rf(app_cache_path)
|
|
87
|
+
FileUtils.cp_r("#{@original_path}/.", app_cache_path)
|
|
88
|
+
FileUtils.touch(app_cache_path.join(".caratcache"))
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def local_specs(*)
|
|
92
|
+
@local_specs ||= load_spec_files
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def specs
|
|
96
|
+
if has_app_cache?
|
|
97
|
+
@path = app_cache_path
|
|
98
|
+
@expanded_path = nil # Invalidate
|
|
99
|
+
end
|
|
100
|
+
local_specs
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def app_cache_dirname
|
|
104
|
+
name
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
def expanded_path
|
|
110
|
+
@expanded_path ||= expand(path)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def expand(somepath)
|
|
114
|
+
somepath.expand_path(Carat.root)
|
|
115
|
+
rescue ArgumentError => e
|
|
116
|
+
Carat.ui.debug(e)
|
|
117
|
+
raise PathError, "There was an error while trying to use the path " \
|
|
118
|
+
"`#{somepath}`.\nThe error message was: #{e.message}."
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def app_cache_path(custom_path = nil)
|
|
122
|
+
@app_cache_path ||= Carat.app_cache(custom_path).join(app_cache_dirname)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def has_app_cache?
|
|
126
|
+
SharedHelpers.in_bundle? && app_cache_path.exist?
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def load_spec_files
|
|
130
|
+
index = Index.new
|
|
131
|
+
|
|
132
|
+
if File.directory?(expanded_path)
|
|
133
|
+
# We sort depth-first since `<<` will override the earlier-found specs
|
|
134
|
+
Dir["#{expanded_path}/#{@glob}"].sort_by { |p| -p.split(File::SEPARATOR).size }.each do |file|
|
|
135
|
+
spec = Carat.load_gemspec(file)
|
|
136
|
+
if spec
|
|
137
|
+
spec.loaded_from = file.to_s
|
|
138
|
+
spec.source = self
|
|
139
|
+
index << spec
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
if index.empty? && @name && @version
|
|
144
|
+
index << Gem::Specification.new do |s|
|
|
145
|
+
s.name = @name
|
|
146
|
+
s.source = self
|
|
147
|
+
s.version = Gem::Version.new(@version)
|
|
148
|
+
s.platform = Gem::Platform::RUBY
|
|
149
|
+
s.summary = "Fake gemspec for #{@name}"
|
|
150
|
+
s.relative_loaded_from = "#{@name}.gemspec"
|
|
151
|
+
s.authors = ["no one"]
|
|
152
|
+
if expanded_path.join("bin").exist?
|
|
153
|
+
executables = expanded_path.join("bin").children
|
|
154
|
+
executables.reject!{|p| File.directory?(p) }
|
|
155
|
+
s.executables = executables.map{|c| c.basename.to_s }
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
elsif File.exist?(expanded_path)
|
|
160
|
+
raise PathError, "The path `#{expanded_path}` is not a directory."
|
|
161
|
+
else
|
|
162
|
+
raise PathError, "The path `#{expanded_path}` does not exist."
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
index
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def relative_path
|
|
169
|
+
if path.to_s.match(%r{^#{Regexp.escape Carat.root.to_s}})
|
|
170
|
+
return path.relative_path_from(Carat.root)
|
|
171
|
+
end
|
|
172
|
+
path
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def generate_bin(spec, disable_extensions = false)
|
|
176
|
+
gem_dir = Pathname.new(spec.full_gem_path)
|
|
177
|
+
|
|
178
|
+
# Some gem authors put absolute paths in their gemspec
|
|
179
|
+
# and we have to save them from themselves
|
|
180
|
+
spec.files = spec.files.map do |p|
|
|
181
|
+
next if File.directory?(p)
|
|
182
|
+
begin
|
|
183
|
+
Pathname.new(p).relative_path_from(gem_dir).to_s
|
|
184
|
+
rescue ArgumentError
|
|
185
|
+
p
|
|
186
|
+
end
|
|
187
|
+
end.compact
|
|
188
|
+
|
|
189
|
+
SharedHelpers.chdir(gem_dir) do
|
|
190
|
+
installer = Path::Installer.new(spec, :env_shebang => false)
|
|
191
|
+
run_hooks(:pre_install, installer)
|
|
192
|
+
installer.build_extensions unless disable_extensions
|
|
193
|
+
run_hooks(:post_build, installer)
|
|
194
|
+
installer.generate_bin
|
|
195
|
+
run_hooks(:post_install, installer)
|
|
196
|
+
end
|
|
197
|
+
rescue Gem::InvalidSpecificationException => e
|
|
198
|
+
Carat.ui.warn "\n#{spec.name} at #{spec.full_gem_path} did not have a valid gemspec.\n" \
|
|
199
|
+
"This prevents carat from installing bins or native extensions, but " \
|
|
200
|
+
"that may not affect its functionality."
|
|
201
|
+
|
|
202
|
+
if !spec.extensions.empty? && !spec.email.empty?
|
|
203
|
+
Carat.ui.warn "If you need to use this package without installing it from a gem " \
|
|
204
|
+
"repository, please contact #{spec.email} and ask them " \
|
|
205
|
+
"to modify their .gemspec so it can work with `gem build`."
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
Carat.ui.warn "The validation message from Rubygems was:\n #{e.message}"
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def run_hooks(type, installer)
|
|
212
|
+
hooks_meth = "#{type}_hooks"
|
|
213
|
+
return unless Gem.respond_to?(hooks_meth)
|
|
214
|
+
Gem.send(hooks_meth).each do |hook|
|
|
215
|
+
result = hook.call(installer)
|
|
216
|
+
if result == false
|
|
217
|
+
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
|
|
218
|
+
message = "#{type} hook#{location} failed for #{installer.spec.full_name}"
|
|
219
|
+
raise InstallHookError, message
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
end
|
|
226
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Carat
|
|
2
|
+
class Source
|
|
3
|
+
class Path
|
|
4
|
+
|
|
5
|
+
class Installer < Carat::GemInstaller
|
|
6
|
+
attr_reader :spec
|
|
7
|
+
|
|
8
|
+
def initialize(spec, options = {})
|
|
9
|
+
@spec = spec
|
|
10
|
+
@gem_dir = Carat.rubygems.path(spec.full_gem_path)
|
|
11
|
+
@wrappers = options[:wrappers] || true
|
|
12
|
+
@env_shebang = options[:env_shebang] || true
|
|
13
|
+
@format_executable = options[:format_executable] || false
|
|
14
|
+
@build_args = options[:build_args] || Carat.rubygems.build_args
|
|
15
|
+
@gem_bin_dir = "#{Carat.rubygems.gem_dir}/bin"
|
|
16
|
+
|
|
17
|
+
if Carat.requires_sudo?
|
|
18
|
+
@tmp_dir = Carat.tmp(spec.full_name).to_s
|
|
19
|
+
@bin_dir = "#{@tmp_dir}/bin"
|
|
20
|
+
else
|
|
21
|
+
@bin_dir = @gem_bin_dir
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def generate_bin
|
|
26
|
+
return if spec.executables.nil? || spec.executables.empty?
|
|
27
|
+
|
|
28
|
+
super
|
|
29
|
+
|
|
30
|
+
if Carat.requires_sudo?
|
|
31
|
+
Carat.mkdir_p @gem_bin_dir
|
|
32
|
+
spec.executables.each do |exe|
|
|
33
|
+
Carat.sudo "cp -R #{@bin_dir}/#{exe} #{@gem_bin_dir}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
ensure
|
|
37
|
+
Carat.rm_rf(@tmp_dir) if Carat.requires_sudo?
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
require 'uri'
|
|
2
|
+
require 'rubygems/user_interaction'
|
|
3
|
+
require 'rubygems/spec_fetcher'
|
|
4
|
+
|
|
5
|
+
module Carat
|
|
6
|
+
class Source
|
|
7
|
+
class Rubygems < Source
|
|
8
|
+
# Use the API when installing less than X gems
|
|
9
|
+
API_REQUEST_LIMIT = 500
|
|
10
|
+
# Ask for X gems per API request
|
|
11
|
+
API_REQUEST_SIZE = 50
|
|
12
|
+
|
|
13
|
+
attr_reader :remotes, :caches
|
|
14
|
+
|
|
15
|
+
def initialize(options = {})
|
|
16
|
+
@options = options
|
|
17
|
+
@remotes = []
|
|
18
|
+
@dependency_names = []
|
|
19
|
+
@allow_remote = false
|
|
20
|
+
@allow_cached = false
|
|
21
|
+
@caches = [Carat.app_cache, *Carat.rubygems.gem_cache]
|
|
22
|
+
|
|
23
|
+
Array(options["remotes"] || []).reverse_each{|r| add_remote(r) }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def remote!
|
|
27
|
+
@allow_remote = true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def cached!
|
|
31
|
+
@allow_cached = true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def hash
|
|
35
|
+
@remotes.hash
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def eql?(o)
|
|
39
|
+
o.is_a?(Rubygems) && o.credless_remotes == credless_remotes
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
alias == eql?
|
|
43
|
+
|
|
44
|
+
def include?(o)
|
|
45
|
+
o.is_a?(Rubygems) && (o.credless_remotes - credless_remotes).empty?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def can_lock?(spec)
|
|
49
|
+
spec.source.is_a?(Rubygems)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def options
|
|
53
|
+
{ "remotes" => @remotes.map { |r| r.to_s } }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.from_lock(options)
|
|
57
|
+
new(options)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def to_lock
|
|
61
|
+
out = "GEM\n"
|
|
62
|
+
remotes.reverse_each do |remote|
|
|
63
|
+
out << " remote: #{suppress_configured_credentials remote}\n"
|
|
64
|
+
end
|
|
65
|
+
out << " specs:\n"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def to_s
|
|
69
|
+
remote_names = self.remotes.map { |r| r.to_s }.join(', ')
|
|
70
|
+
"rubygems repository #{remote_names}"
|
|
71
|
+
end
|
|
72
|
+
alias_method :name, :to_s
|
|
73
|
+
|
|
74
|
+
def specs
|
|
75
|
+
@specs ||= begin
|
|
76
|
+
# remote_specs usually generates a way larger Index than the other
|
|
77
|
+
# sources, and large_idx.use small_idx is way faster than
|
|
78
|
+
# small_idx.use large_idx.
|
|
79
|
+
idx = @allow_remote ? remote_specs.dup : Index.new
|
|
80
|
+
idx.use(cached_specs, :override_dupes) if @allow_cached || @allow_remote
|
|
81
|
+
idx.use(installed_specs, :override_dupes)
|
|
82
|
+
idx
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def install(spec)
|
|
87
|
+
return ["Using #{version_message(spec)}", nil] if installed_specs[spec].any?
|
|
88
|
+
|
|
89
|
+
# Download the gem to get the spec, because some specs that are returned
|
|
90
|
+
# by rubygems.org are broken and wrong.
|
|
91
|
+
if spec.source_uri
|
|
92
|
+
# Check for this spec from other sources
|
|
93
|
+
uris = [spec.source_uri.without_credentials]
|
|
94
|
+
uris += source_uris_for_spec(spec)
|
|
95
|
+
uris.uniq!
|
|
96
|
+
Installer.ambiguous_gems << [spec.name, *uris] if uris.length > 1
|
|
97
|
+
|
|
98
|
+
s = Carat.rubygems.spec_from_gem(fetch_gem(spec), Carat.settings["trust-policy"])
|
|
99
|
+
spec.__swap__(s)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
unless Carat.settings[:no_install]
|
|
103
|
+
path = cached_gem(spec)
|
|
104
|
+
if Carat.requires_sudo?
|
|
105
|
+
install_path = Carat.tmp(spec.full_name)
|
|
106
|
+
bin_path = install_path.join("bin")
|
|
107
|
+
else
|
|
108
|
+
install_path = Carat.rubygems.gem_dir
|
|
109
|
+
bin_path = Carat.system_bindir
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
installed_spec = nil
|
|
113
|
+
Carat.rubygems.preserve_paths do
|
|
114
|
+
installed_spec = Carat::GemInstaller.new(path,
|
|
115
|
+
:install_dir => install_path.to_s,
|
|
116
|
+
:bin_dir => bin_path.to_s,
|
|
117
|
+
:ignore_dependencies => true,
|
|
118
|
+
:wrappers => true,
|
|
119
|
+
:env_shebang => true
|
|
120
|
+
).install
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# SUDO HAX
|
|
124
|
+
if Carat.requires_sudo?
|
|
125
|
+
Carat.rubygems.repository_subdirectories.each do |name|
|
|
126
|
+
src = File.join(install_path, name, "*")
|
|
127
|
+
dst = File.join(Carat.rubygems.gem_dir, name)
|
|
128
|
+
if name == "extensions" && Dir.glob(src).any?
|
|
129
|
+
src = File.join(src, "*/*")
|
|
130
|
+
ext_src = Dir.glob(src).first
|
|
131
|
+
ext_src.gsub!(src[0..-6], '')
|
|
132
|
+
dst = File.dirname(File.join(dst, ext_src))
|
|
133
|
+
end
|
|
134
|
+
Carat.mkdir_p dst
|
|
135
|
+
Carat.sudo "cp -R #{src} #{dst}" if Dir[src].any?
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
spec.executables.each do |exe|
|
|
139
|
+
Carat.mkdir_p Carat.system_bindir
|
|
140
|
+
Carat.sudo "cp -R #{install_path}/bin/#{exe} #{Carat.system_bindir}/"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
installed_spec.loaded_from = loaded_from(spec)
|
|
144
|
+
end
|
|
145
|
+
spec.loaded_from = loaded_from(spec)
|
|
146
|
+
["Installing #{version_message(spec)}", spec.post_install_message]
|
|
147
|
+
ensure
|
|
148
|
+
Carat.rm_rf(install_path) if Carat.requires_sudo?
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def cache(spec, custom_path = nil)
|
|
152
|
+
if builtin_gem?(spec)
|
|
153
|
+
cached_path = cached_built_in_gem(spec)
|
|
154
|
+
else
|
|
155
|
+
cached_path = cached_gem(spec)
|
|
156
|
+
end
|
|
157
|
+
raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path
|
|
158
|
+
return if File.dirname(cached_path) == Carat.app_cache.to_s
|
|
159
|
+
Carat.ui.info " * #{File.basename(cached_path)}"
|
|
160
|
+
FileUtils.cp(cached_path, Carat.app_cache(custom_path))
|
|
161
|
+
rescue Errno::EACCES => e
|
|
162
|
+
Carat.ui.debug(e)
|
|
163
|
+
raise InstallError, e.message
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def cached_built_in_gem(spec)
|
|
167
|
+
cached_path = cached_path(spec)
|
|
168
|
+
if cached_path.nil?
|
|
169
|
+
remote_spec = remote_specs.search(spec).first
|
|
170
|
+
cached_path = fetch_gem(remote_spec)
|
|
171
|
+
end
|
|
172
|
+
cached_path
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def add_remote(source)
|
|
176
|
+
uri = normalize_uri(source)
|
|
177
|
+
@remotes.unshift(uri) unless @remotes.include?(uri)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def replace_remotes(other_remotes)
|
|
181
|
+
return false if other_remotes == @remotes
|
|
182
|
+
|
|
183
|
+
@remotes = []
|
|
184
|
+
other_remotes.reverse_each do |r|
|
|
185
|
+
add_remote r.to_s
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def unmet_deps
|
|
190
|
+
if @allow_remote && api_fetchers.any?
|
|
191
|
+
remote_specs.unmet_dependency_names
|
|
192
|
+
else
|
|
193
|
+
[]
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def fetchers
|
|
198
|
+
@fetchers ||= remotes.map do |uri|
|
|
199
|
+
Carat::Fetcher.new(uri)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
protected
|
|
204
|
+
|
|
205
|
+
def credless_remotes
|
|
206
|
+
remotes.map(&method(:suppress_configured_credentials))
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
private
|
|
210
|
+
|
|
211
|
+
def source_uris_for_spec(spec)
|
|
212
|
+
specs.search_all(spec.name).inject([]) do |uris, s|
|
|
213
|
+
uris << s.source_uri.without_credentials if s.source_uri
|
|
214
|
+
uris
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def loaded_from(spec)
|
|
219
|
+
"#{Carat.rubygems.gem_dir}/specifications/#{spec.full_name}.gemspec"
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def cached_gem(spec)
|
|
223
|
+
cached_gem = cached_path(spec)
|
|
224
|
+
unless cached_gem
|
|
225
|
+
raise Carat::GemNotFound, "Could not find #{spec.file_name} for installation"
|
|
226
|
+
end
|
|
227
|
+
cached_gem
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def cached_path(spec)
|
|
231
|
+
possibilities = @caches.map { |p| "#{p}/#{spec.file_name}" }
|
|
232
|
+
possibilities.find { |p| File.exist?(p) }
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def normalize_uri(uri)
|
|
236
|
+
uri = uri.to_s
|
|
237
|
+
uri = "#{uri}/" unless uri =~ %r'/$'
|
|
238
|
+
uri = URI(uri)
|
|
239
|
+
raise ArgumentError, "The source must be an absolute URI" unless uri.absolute?
|
|
240
|
+
uri
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def suppress_configured_credentials(remote)
|
|
244
|
+
remote_nouser = remote.dup.tap { |uri| uri.user = uri.password = nil }.to_s
|
|
245
|
+
if remote.userinfo && remote.userinfo == Carat.settings[remote_nouser]
|
|
246
|
+
remote_nouser
|
|
247
|
+
else
|
|
248
|
+
remote
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def installed_specs
|
|
253
|
+
@installed_specs ||= begin
|
|
254
|
+
idx = Index.new
|
|
255
|
+
have_carat = false
|
|
256
|
+
Carat.rubygems.all_specs.reverse.each do |spec|
|
|
257
|
+
next if spec.name == 'carat' && spec.version.to_s != VERSION
|
|
258
|
+
have_carat = true if spec.name == 'carat'
|
|
259
|
+
spec.source = self
|
|
260
|
+
idx << spec
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Always have carat locally
|
|
264
|
+
unless have_carat
|
|
265
|
+
# We're running carat directly from the source
|
|
266
|
+
# so, let's create a fake gemspec for it (it's a path)
|
|
267
|
+
# gemspec
|
|
268
|
+
carat = Gem::Specification.new do |s|
|
|
269
|
+
s.name = 'carat'
|
|
270
|
+
s.version = VERSION
|
|
271
|
+
s.platform = Gem::Platform::RUBY
|
|
272
|
+
s.source = self
|
|
273
|
+
s.authors = ["carat team"]
|
|
274
|
+
s.loaded_from = File.expand_path("..", __FILE__)
|
|
275
|
+
end
|
|
276
|
+
idx << carat
|
|
277
|
+
end
|
|
278
|
+
idx
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def cached_specs
|
|
283
|
+
@cached_specs ||= begin
|
|
284
|
+
idx = installed_specs.dup
|
|
285
|
+
|
|
286
|
+
path = Carat.app_cache
|
|
287
|
+
Dir["#{path}/*.gem"].each do |gemfile|
|
|
288
|
+
next if gemfile =~ /^carat\-[\d\.]+?\.gem/
|
|
289
|
+
s ||= Carat.rubygems.spec_from_gem(gemfile)
|
|
290
|
+
s.source = self
|
|
291
|
+
idx << s
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
idx
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def api_fetchers
|
|
299
|
+
fetchers.select{|f| f.use_api }
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def remote_specs
|
|
303
|
+
@remote_specs ||= Index.build do |idx|
|
|
304
|
+
index_fetchers = fetchers - api_fetchers
|
|
305
|
+
|
|
306
|
+
# gather lists from non-api sites
|
|
307
|
+
index_fetchers.each do |f|
|
|
308
|
+
Carat.ui.info "Fetching source index from #{f.uri}"
|
|
309
|
+
idx.use f.specs(nil, self)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# because ensuring we have all the gems we need involves downloading
|
|
313
|
+
# the gemspecs of those gems, if the non-api sites contain more than
|
|
314
|
+
# about 100 gems, we just treat all sites as non-api for speed.
|
|
315
|
+
allow_api = idx.size < API_REQUEST_LIMIT && dependency_names.size < API_REQUEST_LIMIT
|
|
316
|
+
Carat.ui.debug "Need to query more than #{API_REQUEST_LIMIT} gems." \
|
|
317
|
+
" Downloading full index instead..." unless allow_api
|
|
318
|
+
|
|
319
|
+
if allow_api
|
|
320
|
+
api_fetchers.each do |f|
|
|
321
|
+
Carat.ui.info "Fetching gem metadata from #{f.uri}", Carat.ui.debug?
|
|
322
|
+
idx.use f.specs(dependency_names, self)
|
|
323
|
+
Carat.ui.info "" if !Carat.ui.debug? # new line now that the dots are over
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Suppose the gem Foo depends on the gem Bar. Foo exists in Source A. Bar has some versions that exist in both
|
|
327
|
+
# sources A and B. At this point, the API request will have found all the versions of Bar in source A,
|
|
328
|
+
# but will not have found any versions of Bar from source B, which is a problem if the requested version
|
|
329
|
+
# of Foo specifically depends on a version of Bar that is only found in source B. This ensures that for
|
|
330
|
+
# each spec we found, we add all possible versions from all sources to the index.
|
|
331
|
+
begin
|
|
332
|
+
idxcount = idx.size
|
|
333
|
+
api_fetchers.each do |f|
|
|
334
|
+
Carat.ui.info "Fetching version metadata from #{f.uri}", Carat.ui.debug?
|
|
335
|
+
idx.use f.specs(idx.dependency_names, self), true
|
|
336
|
+
Carat.ui.info "" if !Carat.ui.debug? # new line now that the dots are over
|
|
337
|
+
end
|
|
338
|
+
end until idxcount == idx.size
|
|
339
|
+
|
|
340
|
+
if api_fetchers.any? && api_fetchers.all?{|f| f.use_api }
|
|
341
|
+
# it's possible that gems from one source depend on gems from some
|
|
342
|
+
# other source, so now we download gemspecs and iterate over those
|
|
343
|
+
# dependencies, looking for gems we don't have info on yet.
|
|
344
|
+
unmet = idx.unmet_dependency_names
|
|
345
|
+
|
|
346
|
+
# if there are any cross-site gems we missed, get them now
|
|
347
|
+
api_fetchers.each do |f|
|
|
348
|
+
Carat.ui.info "Fetching dependency metadata from #{f.uri}", Carat.ui.debug?
|
|
349
|
+
idx.use f.specs(unmet, self)
|
|
350
|
+
Carat.ui.info "" if !Carat.ui.debug? # new line now that the dots are over
|
|
351
|
+
end if unmet.any?
|
|
352
|
+
else
|
|
353
|
+
allow_api = false
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
if !allow_api
|
|
358
|
+
api_fetchers.each do |f|
|
|
359
|
+
Carat.ui.info "Fetching source index from #{f.uri}"
|
|
360
|
+
idx.use f.specs(nil, self)
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def fetch_gem(spec)
|
|
367
|
+
return false unless spec.source_uri
|
|
368
|
+
Fetcher.download_gem_from_uri(spec, spec.source_uri.original_uri)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def builtin_gem?(spec)
|
|
372
|
+
# Ruby 2.1, where all included gems have this summary
|
|
373
|
+
return true if spec.summary =~ /is bundled with Ruby/
|
|
374
|
+
|
|
375
|
+
# Ruby 2.0, where gemspecs are stored in specifications/default/
|
|
376
|
+
spec.loaded_from && spec.loaded_from.include?("specifications/default/")
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
end
|