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,73 @@
|
|
|
1
|
+
module Carat
|
|
2
|
+
class CLI::Update
|
|
3
|
+
attr_reader :options, :gems
|
|
4
|
+
def initialize(options, gems)
|
|
5
|
+
@options = options
|
|
6
|
+
@gems = gems
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def run
|
|
10
|
+
Carat.ui.level = "error" if options[:quiet]
|
|
11
|
+
|
|
12
|
+
sources = Array(options[:source])
|
|
13
|
+
groups = Array(options[:group]).map(&:to_sym)
|
|
14
|
+
|
|
15
|
+
if gems.empty? && sources.empty? && groups.empty?
|
|
16
|
+
# We're doing a full update
|
|
17
|
+
Carat.definition(true)
|
|
18
|
+
else
|
|
19
|
+
unless Carat.default_lockfile.exist?
|
|
20
|
+
raise GemfileLockNotFound, "This Bundle hasn't been installed yet. " \
|
|
21
|
+
"Run `carat install` to update and install the bundled gems."
|
|
22
|
+
end
|
|
23
|
+
# cycle through the requested gems, just to make sure they exist
|
|
24
|
+
names = Carat.locked_gems.specs.map{ |s| s.name }
|
|
25
|
+
gems.each do |g|
|
|
26
|
+
next if names.include?(g)
|
|
27
|
+
require "carat/cli/common"
|
|
28
|
+
raise GemNotFound, Carat::CLI::Common.gem_not_found_message(g, names)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
if groups.any?
|
|
32
|
+
specs = Carat.definition.specs_for groups
|
|
33
|
+
sources.concat(specs.map(&:name))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
Carat.definition(:gems => gems, :sources => sources)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Carat::Fetcher.disable_endpoint = options["full-index"]
|
|
40
|
+
|
|
41
|
+
opts = options.dup
|
|
42
|
+
opts["update"] = true
|
|
43
|
+
opts["local"] = options[:local]
|
|
44
|
+
|
|
45
|
+
Carat.settings[:jobs] = opts["jobs"] if opts["jobs"]
|
|
46
|
+
|
|
47
|
+
# rubygems plugins sometimes hook into the gem install process
|
|
48
|
+
Gem.load_env_plugins if Gem.respond_to?(:load_env_plugins)
|
|
49
|
+
|
|
50
|
+
Carat.definition.validate_ruby!
|
|
51
|
+
Installer.install Carat.root, Carat.definition, opts
|
|
52
|
+
Carat.load.cache if Carat.app_cache.exist?
|
|
53
|
+
|
|
54
|
+
if Carat.settings[:clean] && Carat.settings[:path]
|
|
55
|
+
require "carat/cli/clean"
|
|
56
|
+
Carat::CLI::Clean.new(options).run
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
Carat.ui.confirm "Bundle updated!"
|
|
60
|
+
without_groups_messages
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def without_groups_messages
|
|
66
|
+
if Carat.settings.without.any?
|
|
67
|
+
require "carat/cli/common"
|
|
68
|
+
Carat.ui.confirm Carat::CLI::Common.without_groups_message
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Carat
|
|
2
|
+
class CLI::Viz
|
|
3
|
+
attr_reader :options, :gem_name
|
|
4
|
+
def initialize(options)
|
|
5
|
+
@options = options
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def run
|
|
9
|
+
require 'graphviz'
|
|
10
|
+
output_file = File.expand_path(options[:file])
|
|
11
|
+
graph = Graph.new(Carat.load, output_file, options[:version], options[:requirements], options[:format], options[:without])
|
|
12
|
+
graph.viz
|
|
13
|
+
rescue LoadError => e
|
|
14
|
+
Carat.ui.error e.inspect
|
|
15
|
+
Carat.ui.warn "Make sure you have the graphviz ruby gem. You can install it with:"
|
|
16
|
+
Carat.ui.warn "`gem install ruby-graphviz`"
|
|
17
|
+
rescue StandardError => e
|
|
18
|
+
if e.message =~ /GraphViz not installed or dot not in PATH/
|
|
19
|
+
Carat.ui.error e.message
|
|
20
|
+
Carat.ui.warn "Please install GraphViz. On a Mac with homebrew, you can run `brew install graphviz`."
|
|
21
|
+
else
|
|
22
|
+
raise
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
module Carat
|
|
2
|
+
# Returns current version of Ruby
|
|
3
|
+
#
|
|
4
|
+
# @return [CurrentRuby] Current version of Ruby
|
|
5
|
+
def self.current_ruby
|
|
6
|
+
@current_ruby ||= CurrentRuby.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class CurrentRuby
|
|
10
|
+
def on_18?
|
|
11
|
+
RUBY_VERSION =~ /^1\.8/
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def on_19?
|
|
15
|
+
RUBY_VERSION =~ /^1\.9/
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def on_20?
|
|
19
|
+
RUBY_VERSION =~ /^2\.0/
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def on_21?
|
|
23
|
+
RUBY_VERSION =~ /^2\.1/
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def on_22?
|
|
27
|
+
RUBY_VERSION =~ /^2\.2/
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def ruby?
|
|
31
|
+
!mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def ruby_18?
|
|
35
|
+
ruby? && on_18?
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def ruby_19?
|
|
39
|
+
ruby? && on_19?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def ruby_20?
|
|
43
|
+
ruby? && on_20?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def ruby_21?
|
|
47
|
+
ruby? && on_21?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def ruby_22?
|
|
51
|
+
ruby? && on_22?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def mri?
|
|
55
|
+
!mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def mri_18?
|
|
59
|
+
mri? && on_18?
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def mri_19?
|
|
63
|
+
mri? && on_19?
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def mri_20?
|
|
67
|
+
mri? && on_20?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def mri_21?
|
|
71
|
+
mri? && on_21?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def mri_22?
|
|
75
|
+
mri? && on_22?
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def rbx?
|
|
79
|
+
ruby? && defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def jruby?
|
|
83
|
+
defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def jruby_18?
|
|
87
|
+
jruby? && on_18?
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def jruby_19?
|
|
91
|
+
jruby? && on_19?
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def maglev?
|
|
95
|
+
defined?(RUBY_ENGINE) && RUBY_ENGINE == "maglev"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def mswin?
|
|
99
|
+
Carat::WINDOWS
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def mswin_18?
|
|
103
|
+
mswin? && on_18?
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def mswin_19?
|
|
107
|
+
mswin? && on_19?
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def mswin_20?
|
|
111
|
+
mswin? && on_20?
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def mswin_21?
|
|
115
|
+
mswin? && on_21?
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def mswin_22?
|
|
119
|
+
mswin? && on_22?
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def mswin64?
|
|
123
|
+
Carat::WINDOWS && Gem::Platform.local.os == "mswin64" && Gem::Platform.local.cpu == 'x64'
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def mswin64_19?
|
|
127
|
+
mswin64? && on_19?
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def mswin64_20?
|
|
131
|
+
mswin64? && on_20?
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def mswin64_21?
|
|
135
|
+
mswin64? && on_21?
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def mswin64_22?
|
|
139
|
+
mswin64? && on_22?
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def mingw?
|
|
143
|
+
Carat::WINDOWS && Gem::Platform.local.os == "mingw32" && Gem::Platform.local.cpu != 'x64'
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def mingw_18?
|
|
147
|
+
mingw? && on_18?
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def mingw_19?
|
|
151
|
+
mingw? && on_19?
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def mingw_20?
|
|
155
|
+
mingw? && on_20?
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def mingw_21?
|
|
159
|
+
mingw? && on_21?
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def mingw_22?
|
|
163
|
+
mingw? && on_22?
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def x64_mingw?
|
|
167
|
+
Carat::WINDOWS && Gem::Platform.local.os == "mingw32" && Gem::Platform.local.cpu == 'x64'
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def x64_mingw_20?
|
|
171
|
+
x64_mingw? && on_20?
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def x64_mingw_21?
|
|
175
|
+
x64_mingw? && on_21?
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def x64_mingw_22?
|
|
179
|
+
x64_mingw? && on_22?
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
end
|
|
183
|
+
end
|
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
require "digest/sha1"
|
|
2
|
+
require "set"
|
|
3
|
+
|
|
4
|
+
module Carat
|
|
5
|
+
class Definition
|
|
6
|
+
include GemHelpers
|
|
7
|
+
|
|
8
|
+
attr_reader :dependencies, :platforms, :ruby_version, :locked_deps
|
|
9
|
+
|
|
10
|
+
# Given a gemfile and lockfile creates a Carat definition
|
|
11
|
+
#
|
|
12
|
+
# @param gemfile [Pathname] Path to Gemfile
|
|
13
|
+
# @param lockfile [Pathname,nil] Path to Gemfile.lock
|
|
14
|
+
# @param unlock [Hash, Boolean, nil] Gems that have been requested
|
|
15
|
+
# to be updated or true if all gems should be updated
|
|
16
|
+
# @return [Carat::Definition]
|
|
17
|
+
def self.build(gemfile, lockfile, unlock)
|
|
18
|
+
unlock ||= {}
|
|
19
|
+
gemfile = Pathname.new(gemfile).expand_path
|
|
20
|
+
|
|
21
|
+
unless gemfile.file?
|
|
22
|
+
raise GemfileNotFound, "#{gemfile} not found"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Dsl.evaluate(gemfile, lockfile, unlock)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
# How does the new system work?
|
|
31
|
+
#
|
|
32
|
+
# * Load information from Gemfile and Lockfile
|
|
33
|
+
# * Invalidate stale locked specs
|
|
34
|
+
# * All specs from stale source are stale
|
|
35
|
+
# * All specs that are reachable only through a stale
|
|
36
|
+
# dependency are stale.
|
|
37
|
+
# * If all fresh dependencies are satisfied by the locked
|
|
38
|
+
# specs, then we can try to resolve locally.
|
|
39
|
+
#
|
|
40
|
+
# @param lockfile [Pathname] Path to Gemfile.lock
|
|
41
|
+
# @param dependencies [Array(Carat::Dependency)] array of dependencies from Gemfile
|
|
42
|
+
# @param sources [Carat::SourceList]
|
|
43
|
+
# @param unlock [Hash, Boolean, nil] Gems that have been requested
|
|
44
|
+
# to be updated or true if all gems should be updated
|
|
45
|
+
# @param ruby_version [Carat::RubyVersion, nil] Requested Ruby Version
|
|
46
|
+
def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil)
|
|
47
|
+
@unlocking = unlock == true || !unlock.empty?
|
|
48
|
+
|
|
49
|
+
@dependencies, @sources, @unlock = dependencies, sources, unlock
|
|
50
|
+
@remote = false
|
|
51
|
+
@specs = nil
|
|
52
|
+
@lockfile_contents = ""
|
|
53
|
+
@ruby_version = ruby_version
|
|
54
|
+
|
|
55
|
+
if lockfile && File.exist?(lockfile)
|
|
56
|
+
@lockfile_contents = Carat.read_file(lockfile)
|
|
57
|
+
locked = LockfileParser.new(@lockfile_contents)
|
|
58
|
+
@platforms = locked.platforms
|
|
59
|
+
|
|
60
|
+
if unlock != true
|
|
61
|
+
@locked_deps = locked.dependencies
|
|
62
|
+
@locked_specs = SpecSet.new(locked.specs)
|
|
63
|
+
@locked_sources = locked.sources
|
|
64
|
+
else
|
|
65
|
+
@unlock = {}
|
|
66
|
+
@locked_deps = []
|
|
67
|
+
@locked_specs = SpecSet.new([])
|
|
68
|
+
@locked_sources = []
|
|
69
|
+
end
|
|
70
|
+
else
|
|
71
|
+
@unlock = {}
|
|
72
|
+
@platforms = []
|
|
73
|
+
@locked_deps = []
|
|
74
|
+
@locked_specs = SpecSet.new([])
|
|
75
|
+
@locked_sources = []
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
@unlock[:gems] ||= []
|
|
79
|
+
@unlock[:sources] ||= []
|
|
80
|
+
|
|
81
|
+
current_platform = Carat.rubygems.platforms.map { |p| generic(p) }.compact.last
|
|
82
|
+
@new_platform = !@platforms.include?(current_platform)
|
|
83
|
+
@platforms |= [current_platform]
|
|
84
|
+
|
|
85
|
+
@path_changes = converge_paths
|
|
86
|
+
eager_unlock = expand_dependencies(@unlock[:gems])
|
|
87
|
+
@unlock[:gems] = @locked_specs.for(eager_unlock).map { |s| s.name }
|
|
88
|
+
|
|
89
|
+
@source_changes = converge_sources
|
|
90
|
+
@dependency_changes = converge_dependencies
|
|
91
|
+
@local_changes = converge_locals
|
|
92
|
+
|
|
93
|
+
fixup_dependency_types!
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def fixup_dependency_types!
|
|
97
|
+
# XXX This is a temporary workaround for a bug when using rubygems 1.8.15
|
|
98
|
+
# where Gem::Dependency#== matches Gem::Dependency#type. As the lockfile
|
|
99
|
+
# doesn't carry a notion of the dependency type, if you use
|
|
100
|
+
# add_development_dependency in a gemspec that's loaded with the gemspec
|
|
101
|
+
# directive, the lockfile dependencies and resolved dependencies end up
|
|
102
|
+
# with a mismatch on #type.
|
|
103
|
+
# Test coverage to catch a regression on this is in gemspec_spec.rb
|
|
104
|
+
@dependencies.each do |d|
|
|
105
|
+
if ld = @locked_deps.find { |l| l.name == d.name }
|
|
106
|
+
ld.instance_variable_set(:@type, d.type)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def resolve_with_cache!
|
|
112
|
+
raise "Specs already loaded" if @specs
|
|
113
|
+
sources.cached!
|
|
114
|
+
specs
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def resolve_remotely!
|
|
118
|
+
raise "Specs already loaded" if @specs
|
|
119
|
+
@remote = true
|
|
120
|
+
sources.remote!
|
|
121
|
+
specs
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# For given dependency list returns a SpecSet with Gemspec of all the required
|
|
125
|
+
# dependencies.
|
|
126
|
+
# 1. The method first resolves the dependencies specified in Gemfile
|
|
127
|
+
# 2. After that it tries and fetches gemspec of resolved dependencies
|
|
128
|
+
#
|
|
129
|
+
# @return [Carat::SpecSet]
|
|
130
|
+
def specs
|
|
131
|
+
@specs ||= begin
|
|
132
|
+
specs = resolve.materialize(Carat.settings[:cache_all_platforms] ? dependencies : requested_dependencies)
|
|
133
|
+
|
|
134
|
+
unless specs["carat"].any?
|
|
135
|
+
local = Carat.settings[:frozen] ? rubygems_index : index
|
|
136
|
+
carat = local.search(Gem::Dependency.new('carat', VERSION)).last
|
|
137
|
+
specs["carat"] = carat if carat
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
specs
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def new_specs
|
|
145
|
+
specs - @locked_specs
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def removed_specs
|
|
149
|
+
@locked_specs - specs
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def new_platform?
|
|
153
|
+
@new_platform
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def missing_specs
|
|
157
|
+
missing = []
|
|
158
|
+
resolve.materialize(requested_dependencies, missing)
|
|
159
|
+
missing
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def requested_specs
|
|
163
|
+
@requested_specs ||= begin
|
|
164
|
+
groups = self.groups - Carat.settings.without
|
|
165
|
+
groups.map! { |g| g.to_sym }
|
|
166
|
+
specs_for(groups)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def current_dependencies
|
|
171
|
+
dependencies.reject { |d| !d.should_include? }
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def specs_for(groups)
|
|
175
|
+
deps = dependencies.select { |d| (d.groups & groups).any? }
|
|
176
|
+
deps.delete_if { |d| !d.should_include? }
|
|
177
|
+
specs.for(expand_dependencies(deps))
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Resolve all the dependencies specified in Gemfile. It ensures that
|
|
181
|
+
# dependencies that have been already resolved via locked file and are fresh
|
|
182
|
+
# are reused when resolving dependencies
|
|
183
|
+
#
|
|
184
|
+
# @return [SpecSet] resolved dependencies
|
|
185
|
+
def resolve
|
|
186
|
+
@resolve ||= begin
|
|
187
|
+
last_resolve = converge_locked_specs
|
|
188
|
+
if Carat.settings[:frozen] || (!@unlocking && nothing_changed?)
|
|
189
|
+
last_resolve
|
|
190
|
+
else
|
|
191
|
+
# Run a resolve against the locally available gems
|
|
192
|
+
last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def index
|
|
198
|
+
@index ||= Index.build do |idx|
|
|
199
|
+
dependency_names = @dependencies.map { |d| d.name }
|
|
200
|
+
|
|
201
|
+
sources.all_sources.each do |source|
|
|
202
|
+
source.dependency_names = dependency_names.dup
|
|
203
|
+
idx.add_source source.specs
|
|
204
|
+
dependency_names -= pinned_spec_names(source.specs)
|
|
205
|
+
dependency_names.push(*source.unmet_deps).uniq!
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# used when frozen is enabled so we can find the carat
|
|
211
|
+
# spec, even if (say) a git gem is not checked out.
|
|
212
|
+
def rubygems_index
|
|
213
|
+
@rubygems_index ||= Index.build do |idx|
|
|
214
|
+
sources.rubygems_sources.each do |rubygems|
|
|
215
|
+
idx.add_source rubygems.specs
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def has_rubygems_remotes?
|
|
221
|
+
sources.rubygems_sources.any? {|s| s.remotes.any? }
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def has_local_dependencies?
|
|
225
|
+
!sources.path_sources.empty? || !sources.git_sources.empty?
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def spec_git_paths
|
|
229
|
+
sources.git_sources.map {|s| s.path.to_s }
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def groups
|
|
233
|
+
dependencies.map { |d| d.groups }.flatten.uniq
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def lock(file)
|
|
237
|
+
contents = to_lock
|
|
238
|
+
|
|
239
|
+
# Convert to \r\n if the existing lock has them
|
|
240
|
+
# i.e., Windows with `git config core.autocrlf=true`
|
|
241
|
+
contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match("\r\n")
|
|
242
|
+
|
|
243
|
+
return if @lockfile_contents == contents
|
|
244
|
+
|
|
245
|
+
if Carat.settings[:frozen]
|
|
246
|
+
Carat.ui.error "Cannot write a changed lockfile while frozen."
|
|
247
|
+
return
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
File.open(file, 'wb'){|f| f.puts(contents) }
|
|
251
|
+
rescue Errno::EACCES
|
|
252
|
+
raise Carat::InstallError,
|
|
253
|
+
"There was an error while trying to write to Gemfile.lock. It is likely that \n" \
|
|
254
|
+
"you need to allow write permissions for the file at path: \n" \
|
|
255
|
+
"#{File.expand_path(file)}"
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def to_lock
|
|
259
|
+
out = ""
|
|
260
|
+
|
|
261
|
+
sources.lock_sources.each do |source|
|
|
262
|
+
# Add the source header
|
|
263
|
+
out << source.to_lock
|
|
264
|
+
# Find all specs for this source
|
|
265
|
+
resolve.
|
|
266
|
+
select { |s| source.can_lock?(s) }.
|
|
267
|
+
# This needs to be sorted by full name so that
|
|
268
|
+
# gems with the same name, but different platform
|
|
269
|
+
# are ordered consistently
|
|
270
|
+
sort_by { |s| s.full_name }.
|
|
271
|
+
each do |spec|
|
|
272
|
+
next if spec.name == 'carat'
|
|
273
|
+
out << spec.to_lock
|
|
274
|
+
end
|
|
275
|
+
out << "\n"
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
out << "PLATFORMS\n"
|
|
279
|
+
|
|
280
|
+
platforms.map { |p| p.to_s }.sort.each do |p|
|
|
281
|
+
out << " #{p}\n"
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
out << "\n"
|
|
285
|
+
out << "DEPENDENCIES\n"
|
|
286
|
+
|
|
287
|
+
handled = []
|
|
288
|
+
dependencies.
|
|
289
|
+
sort_by { |d| d.to_s }.
|
|
290
|
+
each do |dep|
|
|
291
|
+
next if handled.include?(dep.name)
|
|
292
|
+
out << dep.to_lock
|
|
293
|
+
handled << dep.name
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
out
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false)
|
|
300
|
+
msg = "You are trying to install in deployment mode after changing\n" \
|
|
301
|
+
"your Gemfile. Run `carat install` elsewhere and add the\n" \
|
|
302
|
+
"updated Gemfile.lock to version control."
|
|
303
|
+
|
|
304
|
+
unless explicit_flag
|
|
305
|
+
msg += "\n\nIf this is a development machine, remove the Gemfile " \
|
|
306
|
+
"freeze \nby running `carat install --no-deployment`."
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
added = []
|
|
310
|
+
deleted = []
|
|
311
|
+
changed = []
|
|
312
|
+
|
|
313
|
+
gemfile_sources = sources.lock_sources
|
|
314
|
+
if @locked_sources != gemfile_sources
|
|
315
|
+
new_sources = gemfile_sources - @locked_sources
|
|
316
|
+
deleted_sources = @locked_sources - gemfile_sources
|
|
317
|
+
|
|
318
|
+
if new_sources.any?
|
|
319
|
+
added.concat new_sources.map { |source| "* source: #{source}" }
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
if deleted_sources.any?
|
|
323
|
+
deleted.concat deleted_sources.map { |source| "* source: #{source}" }
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
new_deps = @dependencies - @locked_deps
|
|
328
|
+
deleted_deps = @locked_deps - @dependencies
|
|
329
|
+
|
|
330
|
+
if new_deps.any?
|
|
331
|
+
added.concat new_deps.map { |d| "* #{pretty_dep(d)}" }
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
if deleted_deps.any?
|
|
335
|
+
deleted.concat deleted_deps.map { |d| "* #{pretty_dep(d)}" }
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
both_sources = Hash.new { |h,k| h[k] = [] }
|
|
339
|
+
@dependencies.each { |d| both_sources[d.name][0] = d }
|
|
340
|
+
@locked_deps.each { |d| both_sources[d.name][1] = d.source }
|
|
341
|
+
|
|
342
|
+
both_sources.each do |name, (dep, lock_source)|
|
|
343
|
+
if (dep.nil? && !lock_source.nil?) || (!dep.nil? && !lock_source.nil? && !lock_source.can_lock?(dep))
|
|
344
|
+
gemfile_source_name = (dep && dep.source) || 'no specified source'
|
|
345
|
+
lockfile_source_name = lock_source || 'no specified source'
|
|
346
|
+
changed << "* #{name} from `#{gemfile_source_name}` to `#{lockfile_source_name}`"
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
|
|
351
|
+
msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
|
|
352
|
+
msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
|
|
353
|
+
msg << "\n"
|
|
354
|
+
|
|
355
|
+
raise ProductionError, msg if added.any? || deleted.any? || changed.any?
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def validate_ruby!
|
|
359
|
+
return unless ruby_version
|
|
360
|
+
|
|
361
|
+
if diff = ruby_version.diff(Carat.ruby_version)
|
|
362
|
+
problem, expected, actual = diff
|
|
363
|
+
|
|
364
|
+
msg = case problem
|
|
365
|
+
when :engine
|
|
366
|
+
"Your Ruby engine is #{actual}, but your Gemfile specified #{expected}"
|
|
367
|
+
when :version
|
|
368
|
+
"Your Ruby version is #{actual}, but your Gemfile specified #{expected}"
|
|
369
|
+
when :engine_version
|
|
370
|
+
"Your #{Carat.ruby_version.engine} version is #{actual}, but your Gemfile specified #{ruby_version.engine} #{expected}"
|
|
371
|
+
when :patchlevel
|
|
372
|
+
if !expected.is_a?(String)
|
|
373
|
+
"The Ruby patchlevel in your Gemfile must be a string"
|
|
374
|
+
else
|
|
375
|
+
"Your Ruby patchlevel is #{actual}, but your Gemfile specified #{expected}"
|
|
376
|
+
end
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
raise RubyVersionMismatch, msg
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
attr_reader :sources
|
|
384
|
+
private :sources
|
|
385
|
+
|
|
386
|
+
private
|
|
387
|
+
|
|
388
|
+
def nothing_changed?
|
|
389
|
+
!@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
def pretty_dep(dep, source = false)
|
|
393
|
+
msg = "#{dep.name}"
|
|
394
|
+
msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default
|
|
395
|
+
msg << " from the `#{dep.source}` source" if source && dep.source
|
|
396
|
+
msg
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# Check if the specs of the given source changed
|
|
400
|
+
# according to the locked source. A block should be
|
|
401
|
+
# in order to specify how the locked version of
|
|
402
|
+
# the source should be found.
|
|
403
|
+
def specs_changed?(source, &block)
|
|
404
|
+
locked = @locked_sources.find(&block)
|
|
405
|
+
|
|
406
|
+
if locked
|
|
407
|
+
unlocking = @locked_specs.any? do |locked_spec|
|
|
408
|
+
locked_spec.source.class == locked.class && locked_spec.source != locked
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
!locked || unlocking || dependencies_for_source_changed?(locked) || source.specs != locked.specs
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def dependencies_for_source_changed?(source)
|
|
416
|
+
deps_for_source = @dependencies.select { |s| s.source == source }
|
|
417
|
+
locked_deps_for_source = @locked_deps.select { |s| s.source == source }
|
|
418
|
+
|
|
419
|
+
deps_for_source != locked_deps_for_source
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
# Get all locals and override their matching sources.
|
|
423
|
+
# Return true if any of the locals changed (for example,
|
|
424
|
+
# they point to a new revision) or depend on new specs.
|
|
425
|
+
def converge_locals
|
|
426
|
+
locals = []
|
|
427
|
+
|
|
428
|
+
Carat.settings.local_overrides.map do |k,v|
|
|
429
|
+
spec = @dependencies.find { |s| s.name == k }
|
|
430
|
+
source = spec && spec.source
|
|
431
|
+
if source && source.respond_to?(:local_override!)
|
|
432
|
+
source.unlock! if @unlock[:gems].include?(spec.name)
|
|
433
|
+
locals << [ source, source.local_override!(v) ]
|
|
434
|
+
end
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
locals.any? do |source, changed|
|
|
438
|
+
changed || specs_changed?(source) { |o| source.class == o.class && source.uri == o.uri }
|
|
439
|
+
end
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
def converge_paths
|
|
443
|
+
sources.path_sources.any? do |source|
|
|
444
|
+
specs_changed?(source) do |ls|
|
|
445
|
+
ls.class == source.class && ls.path == source.path
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
def converge_sources
|
|
451
|
+
changes = false
|
|
452
|
+
|
|
453
|
+
# Get the Rubygems sources from the Gemfile.lock
|
|
454
|
+
locked_gem_sources = @locked_sources.select { |s| s.kind_of?(Source::Rubygems) }
|
|
455
|
+
# Get the Rubygems remotes from the Gemfile
|
|
456
|
+
actual_remotes = sources.rubygems_remotes
|
|
457
|
+
|
|
458
|
+
# If there is a Rubygems source in both
|
|
459
|
+
if !locked_gem_sources.empty? && !actual_remotes.empty?
|
|
460
|
+
locked_gem_sources.each do |locked_gem|
|
|
461
|
+
# Merge the remotes from the Gemfile into the Gemfile.lock
|
|
462
|
+
changes = changes | locked_gem.replace_remotes(actual_remotes)
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
# Replace the sources from the Gemfile with the sources from the Gemfile.lock,
|
|
467
|
+
# if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent
|
|
468
|
+
# source in the Gemfile.lock, use the one from the Gemfile.
|
|
469
|
+
changes = changes | sources.replace_sources!(@locked_sources)
|
|
470
|
+
|
|
471
|
+
sources.all_sources.each do |source|
|
|
472
|
+
# If the source is unlockable and the current command allows an unlock of
|
|
473
|
+
# the source (for example, you are doing a `carat update <foo>` of a git-pinned
|
|
474
|
+
# gem), unlock it. For git sources, this means to unlock the revision, which
|
|
475
|
+
# will cause the `ref` used to be the most recent for the branch (or master) if
|
|
476
|
+
# an explicit `ref` is not used.
|
|
477
|
+
if source.respond_to?(:unlock!) && @unlock[:sources].include?(source.name)
|
|
478
|
+
source.unlock!
|
|
479
|
+
changes = true
|
|
480
|
+
end
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
changes
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
def converge_dependencies
|
|
487
|
+
(@dependencies + @locked_deps).each do |dep|
|
|
488
|
+
if dep.source
|
|
489
|
+
dep.source = sources.get(dep.source)
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
Set.new(@dependencies) != Set.new(@locked_deps)
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# Remove elements from the locked specs that are expired. This will most
|
|
496
|
+
# commonly happen if the Gemfile has changed since the lockfile was last
|
|
497
|
+
# generated
|
|
498
|
+
def converge_locked_specs
|
|
499
|
+
deps = []
|
|
500
|
+
|
|
501
|
+
# Build a list of dependencies that are the same in the Gemfile
|
|
502
|
+
# and Gemfile.lock. If the Gemfile modified a dependency, but
|
|
503
|
+
# the gem in the Gemfile.lock still satisfies it, this is fine
|
|
504
|
+
# too.
|
|
505
|
+
locked_deps_hash = @locked_deps.inject({}) { |hsh, dep| hsh[dep] = dep; hsh }
|
|
506
|
+
@dependencies.each do |dep|
|
|
507
|
+
locked_dep = locked_deps_hash[dep]
|
|
508
|
+
|
|
509
|
+
if in_locked_deps?(dep, locked_dep) || satisfies_locked_spec?(dep)
|
|
510
|
+
deps << dep
|
|
511
|
+
elsif dep.source.is_a?(Source::Path) && dep.current_platform? && (!locked_dep || dep.source != locked_dep.source)
|
|
512
|
+
@locked_specs.each do |s|
|
|
513
|
+
@unlock[:gems] << s.name if s.source == dep.source
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
dep.source.unlock! if dep.source.respond_to?(:unlock!)
|
|
517
|
+
dep.source.specs.each { |s| @unlock[:gems] << s.name }
|
|
518
|
+
end
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
converged = []
|
|
522
|
+
@locked_specs.each do |s|
|
|
523
|
+
# Replace the locked dependency's source with the equivalent source from the Gemfile
|
|
524
|
+
dep = @dependencies.find { |d| s.satisfies?(d) }
|
|
525
|
+
s.source = (dep && dep.source) || sources.get(s.source)
|
|
526
|
+
|
|
527
|
+
# Don't add a spec to the list if its source is expired. For example,
|
|
528
|
+
# if you change a Git gem to Rubygems.
|
|
529
|
+
next if s.source.nil? || @unlock[:sources].include?(s.name)
|
|
530
|
+
# If the spec is from a path source and it doesn't exist anymore
|
|
531
|
+
# then we just unlock it.
|
|
532
|
+
|
|
533
|
+
# Path sources have special logic
|
|
534
|
+
if s.source.instance_of?(Source::Path)
|
|
535
|
+
other = s.source.specs[s].first
|
|
536
|
+
|
|
537
|
+
# If the spec is no longer in the path source, unlock it. This
|
|
538
|
+
# commonly happens if the version changed in the gemspec
|
|
539
|
+
next unless other
|
|
540
|
+
|
|
541
|
+
deps2 = other.dependencies.select { |d| d.type != :development }
|
|
542
|
+
# If the dependencies of the path source have changed, unlock it
|
|
543
|
+
next unless s.dependencies.sort == deps2.sort
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
converged << s
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
resolve = SpecSet.new(converged)
|
|
550
|
+
resolve = resolve.for(expand_dependencies(deps, true), @unlock[:gems])
|
|
551
|
+
diff = @locked_specs.to_a - resolve.to_a
|
|
552
|
+
|
|
553
|
+
# Now, we unlock any sources that do not have anymore gems pinned to it
|
|
554
|
+
sources.all_sources.each do |source|
|
|
555
|
+
next unless source.respond_to?(:unlock!)
|
|
556
|
+
|
|
557
|
+
unless resolve.any? { |s| s.source == source }
|
|
558
|
+
source.unlock! if !diff.empty? && diff.any? { |s| s.source == source }
|
|
559
|
+
end
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
resolve
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
def in_locked_deps?(dep, locked_dep)
|
|
566
|
+
# Because the lockfile can't link a dep to a specific remote, we need to
|
|
567
|
+
# treat sources as equivalent anytime the locked dep has all the remotes
|
|
568
|
+
# that the Gemfile dep does.
|
|
569
|
+
locked_dep && locked_dep.source && dep.source && locked_dep.source.include?(dep.source)
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
def satisfies_locked_spec?(dep)
|
|
573
|
+
@locked_specs.any? { |s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) }
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
def expanded_dependencies
|
|
577
|
+
@expanded_dependencies ||= expand_dependencies(dependencies, @remote)
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
def expand_dependencies(dependencies, remote = false)
|
|
581
|
+
deps = []
|
|
582
|
+
dependencies.each do |dep|
|
|
583
|
+
dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name)
|
|
584
|
+
next unless remote || dep.current_platform?
|
|
585
|
+
dep.gem_platforms(@platforms).each do |p|
|
|
586
|
+
deps << DepProxy.new(dep, p) if remote || p == generic(Gem::Platform.local)
|
|
587
|
+
end
|
|
588
|
+
end
|
|
589
|
+
deps
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
def requested_dependencies
|
|
593
|
+
groups = self.groups - Carat.settings.without
|
|
594
|
+
groups.map! { |g| g.to_sym }
|
|
595
|
+
dependencies.reject { |d| !d.should_include? || (d.groups & groups).empty? }
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
def source_requirements
|
|
599
|
+
# Load all specs from remote sources
|
|
600
|
+
index
|
|
601
|
+
|
|
602
|
+
# Record the specs available in each gem's source, so that those
|
|
603
|
+
# specs will be available later when the resolver knows where to
|
|
604
|
+
# look for that gemspec (or its dependencies)
|
|
605
|
+
source_requirements = {}
|
|
606
|
+
dependencies.each do |dep|
|
|
607
|
+
next unless dep.source
|
|
608
|
+
source_requirements[dep.name] = dep.source.specs
|
|
609
|
+
end
|
|
610
|
+
source_requirements
|
|
611
|
+
end
|
|
612
|
+
|
|
613
|
+
def pinned_spec_names(specs)
|
|
614
|
+
names = []
|
|
615
|
+
specs.each do |s|
|
|
616
|
+
# TODO when two sources without blocks is an error, we can change
|
|
617
|
+
# this check to !s.source.is_a?(Source::LocalRubygems). For now,
|
|
618
|
+
# we need to ask every Rubygems for every gem name.
|
|
619
|
+
if s.source.is_a?(Source::Git) || s.source.is_a?(Source::Path)
|
|
620
|
+
names << s.name
|
|
621
|
+
end
|
|
622
|
+
end
|
|
623
|
+
names.uniq!
|
|
624
|
+
names
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
end
|
|
628
|
+
end
|