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,43 @@
|
|
|
1
|
+
module Carat
|
|
2
|
+
class DepProxy
|
|
3
|
+
|
|
4
|
+
attr_reader :__platform, :dep
|
|
5
|
+
|
|
6
|
+
def initialize(dep, platform)
|
|
7
|
+
@dep, @__platform = dep, platform
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def hash
|
|
11
|
+
@hash ||= dep.hash
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def ==(o)
|
|
15
|
+
dep == o.dep && __platform == o.__platform
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
alias eql? ==
|
|
19
|
+
|
|
20
|
+
def type
|
|
21
|
+
@dep.type
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def name
|
|
25
|
+
@dep.name
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def requirement
|
|
29
|
+
@dep.requirement
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def to_s
|
|
33
|
+
"#{name} (#{requirement}) #{__platform}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def method_missing(*args)
|
|
39
|
+
@dep.send(*args)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
require 'rubygems/dependency'
|
|
2
|
+
require 'carat/shared_helpers'
|
|
3
|
+
require 'carat/rubygems_ext'
|
|
4
|
+
|
|
5
|
+
module Carat
|
|
6
|
+
class Dependency < Gem::Dependency
|
|
7
|
+
attr_reader :autorequire
|
|
8
|
+
attr_reader :groups
|
|
9
|
+
attr_reader :platforms
|
|
10
|
+
|
|
11
|
+
PLATFORM_MAP = {
|
|
12
|
+
:ruby => Gem::Platform::RUBY,
|
|
13
|
+
:ruby_18 => Gem::Platform::RUBY,
|
|
14
|
+
:ruby_19 => Gem::Platform::RUBY,
|
|
15
|
+
:ruby_20 => Gem::Platform::RUBY,
|
|
16
|
+
:ruby_21 => Gem::Platform::RUBY,
|
|
17
|
+
:ruby_22 => Gem::Platform::RUBY,
|
|
18
|
+
:mri => Gem::Platform::RUBY,
|
|
19
|
+
:mri_18 => Gem::Platform::RUBY,
|
|
20
|
+
:mri_19 => Gem::Platform::RUBY,
|
|
21
|
+
:mri_20 => Gem::Platform::RUBY,
|
|
22
|
+
:mri_21 => Gem::Platform::RUBY,
|
|
23
|
+
:mri_22 => Gem::Platform::RUBY,
|
|
24
|
+
:rbx => Gem::Platform::RUBY,
|
|
25
|
+
:jruby => Gem::Platform::JAVA,
|
|
26
|
+
:jruby_18 => Gem::Platform::JAVA,
|
|
27
|
+
:jruby_19 => Gem::Platform::JAVA,
|
|
28
|
+
:mswin => Gem::Platform::MSWIN,
|
|
29
|
+
:mswin_18 => Gem::Platform::MSWIN,
|
|
30
|
+
:mswin_19 => Gem::Platform::MSWIN,
|
|
31
|
+
:mswin_20 => Gem::Platform::MSWIN,
|
|
32
|
+
:mswin_21 => Gem::Platform::MSWIN,
|
|
33
|
+
:mswin64 => Gem::Platform::MSWIN64,
|
|
34
|
+
:mswin64_19 => Gem::Platform::MSWIN64,
|
|
35
|
+
:mswin64_20 => Gem::Platform::MSWIN64,
|
|
36
|
+
:mswin64_21 => Gem::Platform::MSWIN64,
|
|
37
|
+
:mingw => Gem::Platform::MINGW,
|
|
38
|
+
:mingw_18 => Gem::Platform::MINGW,
|
|
39
|
+
:mingw_19 => Gem::Platform::MINGW,
|
|
40
|
+
:mingw_20 => Gem::Platform::MINGW,
|
|
41
|
+
:mingw_21 => Gem::Platform::MINGW,
|
|
42
|
+
:mingw_22 => Gem::Platform::MINGW,
|
|
43
|
+
:x64_mingw => Gem::Platform::X64_MINGW,
|
|
44
|
+
:x64_mingw_20 => Gem::Platform::X64_MINGW,
|
|
45
|
+
:x64_mingw_21 => Gem::Platform::X64_MINGW,
|
|
46
|
+
:x64_mingw_22 => Gem::Platform::X64_MINGW
|
|
47
|
+
}.freeze
|
|
48
|
+
|
|
49
|
+
def initialize(name, version, options = {}, &blk)
|
|
50
|
+
type = options["type"] || :runtime
|
|
51
|
+
super(name, version, type)
|
|
52
|
+
|
|
53
|
+
@autorequire = nil
|
|
54
|
+
@groups = Array(options["group"] || :default).map { |g| g.to_sym }
|
|
55
|
+
@source = options["source"]
|
|
56
|
+
@platforms = Array(options["platforms"])
|
|
57
|
+
@env = options["env"]
|
|
58
|
+
|
|
59
|
+
if options.key?('require')
|
|
60
|
+
@autorequire = Array(options['require'] || [])
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def gem_platforms(valid_platforms)
|
|
65
|
+
return valid_platforms if @platforms.empty?
|
|
66
|
+
|
|
67
|
+
platforms = []
|
|
68
|
+
@platforms.each do |p|
|
|
69
|
+
platform = PLATFORM_MAP[p]
|
|
70
|
+
next unless valid_platforms.include?(platform)
|
|
71
|
+
platforms |= [platform]
|
|
72
|
+
end
|
|
73
|
+
platforms
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def should_include?
|
|
77
|
+
current_env? && current_platform?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def current_env?
|
|
81
|
+
return true unless @env
|
|
82
|
+
if @env.is_a?(Hash)
|
|
83
|
+
@env.all? do |key, val|
|
|
84
|
+
ENV[key.to_s] && (val.is_a?(String) ? ENV[key.to_s] == val : ENV[key.to_s] =~ val)
|
|
85
|
+
end
|
|
86
|
+
else
|
|
87
|
+
ENV[@env.to_s]
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def current_platform?
|
|
92
|
+
return true if @platforms.empty?
|
|
93
|
+
@platforms.any? { |p|
|
|
94
|
+
Carat.current_ruby.send("#{p}?")
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def to_lock
|
|
99
|
+
out = super
|
|
100
|
+
out << '!' if source
|
|
101
|
+
out << "\n"
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def specific?
|
|
105
|
+
super
|
|
106
|
+
rescue NoMethodError
|
|
107
|
+
requirement != ">= 0"
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Carat
|
|
2
|
+
class Deployment
|
|
3
|
+
def self.define_task(context, task_method = :task, opts = {})
|
|
4
|
+
if defined?(Capistrano) && context.is_a?(Capistrano::Configuration)
|
|
5
|
+
context_name = "capistrano"
|
|
6
|
+
role_default = "{:except => {:no_release => true}}"
|
|
7
|
+
error_type = ::Capistrano::CommandError
|
|
8
|
+
else
|
|
9
|
+
context_name = "vlad"
|
|
10
|
+
role_default = "[:app]"
|
|
11
|
+
error_type = ::Rake::CommandFailedError
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
roles = context.fetch(:bundle_roles, false)
|
|
15
|
+
opts[:roles] = roles if roles
|
|
16
|
+
|
|
17
|
+
context.send :namespace, :bundle do
|
|
18
|
+
send :desc, <<-DESC
|
|
19
|
+
Install the current Carat environment. By default, gems will be \
|
|
20
|
+
installed to the shared/bundle path. Gems in the development and \
|
|
21
|
+
test group will not be installed. The install command is executed \
|
|
22
|
+
with the --deployment and --quiet flags. If the bundle cmd cannot \
|
|
23
|
+
be found then you can override the bundle_cmd variable to specify \
|
|
24
|
+
which one it should use. The base path to the app is fetched from \
|
|
25
|
+
the :latest_release variable. Set it for custom deploy layouts.
|
|
26
|
+
|
|
27
|
+
You can override any of these defaults by setting the variables shown below.
|
|
28
|
+
|
|
29
|
+
N.B. bundle_roles must be defined before you require 'carat/#{context_name}' \
|
|
30
|
+
in your deploy.rb file.
|
|
31
|
+
|
|
32
|
+
set :bundle_gemfile, "Gemfile"
|
|
33
|
+
set :bundle_dir, File.join(fetch(:shared_path), 'carat')
|
|
34
|
+
set :bundle_flags, "--deployment --quiet"
|
|
35
|
+
set :bundle_without, [:development, :test]
|
|
36
|
+
set :bundle_cmd, "carat" # e.g. "/opt/ruby/bin/carat"
|
|
37
|
+
set :bundle_roles, #{role_default} # e.g. [:app, :batch]
|
|
38
|
+
DESC
|
|
39
|
+
send task_method, :install, opts do
|
|
40
|
+
bundle_cmd = context.fetch(:bundle_cmd, "carat")
|
|
41
|
+
bundle_flags = context.fetch(:bundle_flags, "--deployment --quiet")
|
|
42
|
+
bundle_dir = context.fetch(:bundle_dir, File.join(context.fetch(:shared_path), 'carat'))
|
|
43
|
+
bundle_gemfile = context.fetch(:bundle_gemfile, "Gemfile")
|
|
44
|
+
bundle_without = [*context.fetch(:bundle_without, [:development, :test])].compact
|
|
45
|
+
app_path = context.fetch(:latest_release)
|
|
46
|
+
if app_path.to_s.empty?
|
|
47
|
+
raise error_type.new("Cannot detect current release path - make sure you have deployed at least once.")
|
|
48
|
+
end
|
|
49
|
+
args = ["--gemfile #{File.join(app_path, bundle_gemfile)}"]
|
|
50
|
+
args << "--path #{bundle_dir}" unless bundle_dir.to_s.empty?
|
|
51
|
+
args << bundle_flags.to_s
|
|
52
|
+
args << "--without #{bundle_without.join(" ")}" unless bundle_without.empty?
|
|
53
|
+
|
|
54
|
+
run "cd #{app_path} && #{bundle_cmd} install #{args.join(' ')}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Carat
|
|
2
|
+
|
|
3
|
+
if defined? ::Deprecate
|
|
4
|
+
Deprecate = ::Deprecate
|
|
5
|
+
elsif defined? Gem::Deprecate
|
|
6
|
+
Deprecate = Gem::Deprecate
|
|
7
|
+
else
|
|
8
|
+
class Deprecate; end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
unless Deprecate.respond_to?(:skip_during)
|
|
12
|
+
def Deprecate.skip_during; yield; end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
data/lib/carat/dsl.rb
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
require 'carat/dependency'
|
|
2
|
+
require 'carat/ruby_dsl'
|
|
3
|
+
|
|
4
|
+
module Carat
|
|
5
|
+
class Dsl
|
|
6
|
+
include RubyDsl
|
|
7
|
+
|
|
8
|
+
def self.evaluate(gemfile, lockfile, unlock)
|
|
9
|
+
builder = new
|
|
10
|
+
builder.eval_gemfile(gemfile)
|
|
11
|
+
builder.to_definition(lockfile, unlock)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
VALID_PLATFORMS = Carat::Dependency::PLATFORM_MAP.keys.freeze
|
|
15
|
+
|
|
16
|
+
attr_accessor :dependencies
|
|
17
|
+
|
|
18
|
+
def initialize
|
|
19
|
+
@source = nil
|
|
20
|
+
@sources = SourceList.new
|
|
21
|
+
@git_sources = {}
|
|
22
|
+
@dependencies = []
|
|
23
|
+
@groups = []
|
|
24
|
+
@platforms = []
|
|
25
|
+
@env = nil
|
|
26
|
+
@ruby_version = nil
|
|
27
|
+
add_git_sources
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def eval_gemfile(gemfile, contents = nil)
|
|
31
|
+
contents ||= Carat.read_file(gemfile.to_s)
|
|
32
|
+
instance_eval(contents, gemfile.to_s, 1)
|
|
33
|
+
rescue SyntaxError => e
|
|
34
|
+
syntax_msg = e.message.gsub("#{gemfile}:", 'on line ')
|
|
35
|
+
raise GemfileError, "Gemfile syntax error #{syntax_msg}"
|
|
36
|
+
rescue ScriptError, RegexpError, NameError, ArgumentError, RuntimeError => e
|
|
37
|
+
e.backtrace[0] = "#{e.backtrace[0]}: #{e.message} (#{e.class})"
|
|
38
|
+
Carat.ui.warn e.backtrace.join("\n ")
|
|
39
|
+
raise GemfileError, "There was an error in your Gemfile," \
|
|
40
|
+
" and Carat cannot continue."
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def gemspec(opts = nil)
|
|
44
|
+
path = opts && opts[:path] || '.'
|
|
45
|
+
name = opts && opts[:name] || '{,*}'
|
|
46
|
+
development_group = opts && opts[:development_group] || :development
|
|
47
|
+
expanded_path = File.expand_path(path, Carat.default_gemfile.dirname)
|
|
48
|
+
|
|
49
|
+
gemspecs = Dir[File.join(expanded_path, "#{name}.gemspec")]
|
|
50
|
+
|
|
51
|
+
case gemspecs.size
|
|
52
|
+
when 1
|
|
53
|
+
spec = Carat.load_gemspec(gemspecs.first)
|
|
54
|
+
raise InvalidOption, "There was an error loading the gemspec at #{gemspecs.first}." unless spec
|
|
55
|
+
gem spec.name, :path => path
|
|
56
|
+
group(development_group) do
|
|
57
|
+
spec.development_dependencies.each do |dep|
|
|
58
|
+
gem dep.name, *(dep.requirement.as_list + [:type => :development])
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
when 0
|
|
62
|
+
raise InvalidOption, "There are no gemspecs at #{expanded_path}."
|
|
63
|
+
else
|
|
64
|
+
raise InvalidOption, "There are multiple gemspecs at #{expanded_path}. Please use the :name option to specify which one."
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def gem(name, *args)
|
|
69
|
+
options = args.last.is_a?(Hash) ? args.pop.dup : {}
|
|
70
|
+
version = args || [">= 0"]
|
|
71
|
+
|
|
72
|
+
normalize_options(name, version, options)
|
|
73
|
+
|
|
74
|
+
dep = Dependency.new(name, version, options)
|
|
75
|
+
|
|
76
|
+
# if there's already a dependency with this name we try to prefer one
|
|
77
|
+
if current = @dependencies.find { |d| d.name == dep.name }
|
|
78
|
+
if current.requirement != dep.requirement
|
|
79
|
+
if current.type == :development
|
|
80
|
+
@dependencies.delete current
|
|
81
|
+
elsif dep.type == :development
|
|
82
|
+
return
|
|
83
|
+
else
|
|
84
|
+
raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \
|
|
85
|
+
"You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
else
|
|
89
|
+
Carat.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
|
|
90
|
+
"You should probably keep only one of them.\n" \
|
|
91
|
+
"While it's not a problem now, it could cause errors if you change the version of just one of them later."
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
if current.source != dep.source
|
|
95
|
+
if current.type == :development
|
|
96
|
+
@dependencies.delete current
|
|
97
|
+
elsif dep.type == :development
|
|
98
|
+
return
|
|
99
|
+
else
|
|
100
|
+
raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
|
|
101
|
+
"You specified that #{dep.name} (#{dep.requirement}) should come from " \
|
|
102
|
+
"#{current.source || 'an unspecified source'} and #{dep.source}\n"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
@dependencies << dep
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def source(source, &blk)
|
|
111
|
+
source = normalize_source(source)
|
|
112
|
+
if block_given?
|
|
113
|
+
with_source(@sources.add_rubygems_source("remotes" => source), &blk)
|
|
114
|
+
else
|
|
115
|
+
check_primary_source_safety(@sources)
|
|
116
|
+
@sources.add_rubygems_remote(source)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def git_source(name, &block)
|
|
121
|
+
unless block_given?
|
|
122
|
+
raise InvalidOption, "You need to pass a block to #git_source"
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
if valid_keys.include?(name.to_s)
|
|
126
|
+
raise InvalidOption, "You cannot use #{name} as a git source. It " \
|
|
127
|
+
"is a reserved key. Reserved keys are: #{valid_keys.join(", ")}"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
@git_sources[name.to_s] = block
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def path(path, options = {}, &blk)
|
|
134
|
+
with_source(@sources.add_path_source(normalize_hash(options).merge("path" => Pathname.new(path))), &blk)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def git(uri, options = {}, &blk)
|
|
138
|
+
unless block_given?
|
|
139
|
+
msg = "You can no longer specify a git source by itself. Instead, \n" \
|
|
140
|
+
"either use the :git option on a gem, or specify the gems that \n" \
|
|
141
|
+
"carat should find in the git source by passing a block to \n" \
|
|
142
|
+
"the git method, like: \n\n" \
|
|
143
|
+
" git 'git://github.com/rails/rails.git' do\n" \
|
|
144
|
+
" gem 'rails'\n" \
|
|
145
|
+
" end"
|
|
146
|
+
raise DeprecatedError, msg
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
with_source(@sources.add_git_source(normalize_hash(options).merge("uri" => uri)), &blk)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def github(repo, options = {})
|
|
153
|
+
raise ArgumentError, "Github sources require a block" unless block_given?
|
|
154
|
+
github_uri = @git_sources["github"].call(repo)
|
|
155
|
+
git_options = normalize_hash(options).merge("uri" => github_uri)
|
|
156
|
+
git_source = @sources.add_git_source(git_options)
|
|
157
|
+
with_source(git_source) { yield }
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def to_definition(lockfile, unlock)
|
|
161
|
+
Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def group(*args, &blk)
|
|
165
|
+
@groups.concat args
|
|
166
|
+
yield
|
|
167
|
+
ensure
|
|
168
|
+
args.each { @groups.pop }
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def platforms(*platforms)
|
|
172
|
+
@platforms.concat platforms
|
|
173
|
+
yield
|
|
174
|
+
ensure
|
|
175
|
+
platforms.each { @platforms.pop }
|
|
176
|
+
end
|
|
177
|
+
alias_method :platform, :platforms
|
|
178
|
+
|
|
179
|
+
def env(name)
|
|
180
|
+
@env, old = name, @env
|
|
181
|
+
yield
|
|
182
|
+
ensure
|
|
183
|
+
@env = old
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def method_missing(name, *args)
|
|
187
|
+
location = caller[0].split(':')[0..1].join(':')
|
|
188
|
+
raise GemfileError, "Undefined local variable or method `#{name}' for Gemfile\n" \
|
|
189
|
+
" from #{location}"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
private
|
|
193
|
+
|
|
194
|
+
def add_git_sources
|
|
195
|
+
git_source(:github) do |repo_name|
|
|
196
|
+
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
|
|
197
|
+
"git://github.com/#{repo_name}.git"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
git_source(:gist){ |repo_name| "https://gist.github.com/#{repo_name}.git" }
|
|
201
|
+
|
|
202
|
+
git_source(:bitbucket) do |repo_name|
|
|
203
|
+
user_name, repo_name = repo_name.split '/'
|
|
204
|
+
repo_name ||= user_name
|
|
205
|
+
"https://#{user_name}@bitbucket.org/#{user_name}/#{repo_name}.git"
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def with_source(source)
|
|
210
|
+
if block_given?
|
|
211
|
+
@source = source
|
|
212
|
+
yield
|
|
213
|
+
end
|
|
214
|
+
source
|
|
215
|
+
ensure
|
|
216
|
+
@source = nil
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def normalize_hash(opts)
|
|
220
|
+
opts.keys.each do |k|
|
|
221
|
+
opts[k.to_s] = opts.delete(k) unless k.is_a?(String)
|
|
222
|
+
end
|
|
223
|
+
opts
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def valid_keys
|
|
227
|
+
@valid_keys ||= %w(group groups git path name branch ref tag require submodules platform platforms type source)
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def normalize_options(name, version, opts)
|
|
231
|
+
if name.is_a?(Symbol)
|
|
232
|
+
raise GemfileError, %{You need to specify gem names as Strings. Use 'gem "#{name}"' instead.}
|
|
233
|
+
end
|
|
234
|
+
if name =~ /\s/
|
|
235
|
+
raise GemfileError, %{'#{name}' is not a valid gem name because it contains whitespace.}
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
normalize_hash(opts)
|
|
239
|
+
|
|
240
|
+
git_names = @git_sources.keys.map(&:to_s)
|
|
241
|
+
|
|
242
|
+
invalid_keys = opts.keys - (valid_keys + git_names)
|
|
243
|
+
if invalid_keys.any?
|
|
244
|
+
message = "You passed #{invalid_keys.map{|k| ':'+k }.join(", ")} "
|
|
245
|
+
message << if invalid_keys.size > 1
|
|
246
|
+
"as options for gem '#{name}', but they are invalid."
|
|
247
|
+
else
|
|
248
|
+
"as an option for gem '#{name}', but it is invalid."
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
message << " Valid options are: #{valid_keys.join(", ")}"
|
|
252
|
+
raise InvalidOption, message
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
groups = @groups.dup
|
|
256
|
+
opts["group"] = opts.delete("groups") || opts["group"]
|
|
257
|
+
groups.concat Array(opts.delete("group"))
|
|
258
|
+
groups = [:default] if groups.empty?
|
|
259
|
+
|
|
260
|
+
platforms = @platforms.dup
|
|
261
|
+
opts["platforms"] = opts["platform"] || opts["platforms"]
|
|
262
|
+
platforms.concat Array(opts.delete("platforms"))
|
|
263
|
+
platforms.map! { |p| p.to_sym }
|
|
264
|
+
platforms.each do |p|
|
|
265
|
+
next if VALID_PLATFORMS.include?(p)
|
|
266
|
+
raise GemfileError, "`#{p}` is not a valid platform. The available options are: #{VALID_PLATFORMS.inspect}"
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Save sources passed in a key
|
|
270
|
+
if opts.has_key?("source")
|
|
271
|
+
source = normalize_source(opts["source"])
|
|
272
|
+
opts["source"] = @sources.add_rubygems_source("remotes" => source)
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
git_name = (git_names & opts.keys).last
|
|
276
|
+
if @git_sources[git_name]
|
|
277
|
+
opts["git"] = @git_sources[git_name].call(opts[git_name])
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
["git", "path"].each do |type|
|
|
281
|
+
if param = opts[type]
|
|
282
|
+
if version.first && version.first =~ /^\s*=?\s*(\d[^\s]*)\s*$/
|
|
283
|
+
options = opts.merge("name" => name, "version" => $1)
|
|
284
|
+
else
|
|
285
|
+
options = opts.dup
|
|
286
|
+
end
|
|
287
|
+
source = send(type, param, options) {}
|
|
288
|
+
opts["source"] = source
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
opts["source"] ||= @source
|
|
293
|
+
opts["env"] ||= @env
|
|
294
|
+
opts["platforms"] = platforms.dup
|
|
295
|
+
opts["group"] = groups
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def normalize_source(source)
|
|
299
|
+
case source
|
|
300
|
+
when :gemcutter, :rubygems, :rubyforge
|
|
301
|
+
Carat.ui.warn "The source :#{source} is deprecated because HTTP " \
|
|
302
|
+
"requests are insecure.\nPlease change your source to 'https://" \
|
|
303
|
+
"rubygems.org' if possible, or 'http://rubygems.org' if not."
|
|
304
|
+
"http://rubygems.org"
|
|
305
|
+
when String
|
|
306
|
+
source
|
|
307
|
+
else
|
|
308
|
+
raise GemfileError, "Unknown source '#{source}'"
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def check_primary_source_safety(source)
|
|
313
|
+
return unless source.rubygems_primary_remotes.any?
|
|
314
|
+
|
|
315
|
+
if Carat.settings[:disable_multisource]
|
|
316
|
+
raise GemspecError, "Warning: this Gemfile contains multiple primary sources. " \
|
|
317
|
+
"Each source after the first must include a block to indicate which gems " \
|
|
318
|
+
"should come from that source. To downgrade this error to a warning, run " \
|
|
319
|
+
"`carat config --delete disable_multisource`."
|
|
320
|
+
else
|
|
321
|
+
Carat.ui.warn "Warning: this Gemfile contains multiple primary sources. " \
|
|
322
|
+
"Using `source` more than once without a block is a security risk, and " \
|
|
323
|
+
"may result in installing unexpected gems. To resolve this warning, use " \
|
|
324
|
+
"a block to indicate which gems should come from the secondary source. " \
|
|
325
|
+
"To upgrade this warning to an error, run `carat config " \
|
|
326
|
+
"disable_multisource true`."
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
end
|
|
331
|
+
end
|