appraisal 0.5.2 → 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +12 -4
- data/README.md +87 -36
- data/appraisal.gemspec +10 -7
- data/bin/appraisal +7 -0
- data/features/appraisals.feature +16 -2
- data/features/bundler_gemfile_compatibility.feature +70 -0
- data/features/gemspec.feature +0 -11
- data/features/missing_appraisals_file.feature +23 -0
- data/features/step_definitions/dependency_steps.rb +17 -24
- data/features/support/dependency_helpers.rb +29 -0
- data/lib/appraisal/appraisal.rb +52 -12
- data/lib/appraisal/cli.rb +87 -0
- data/lib/appraisal/command.rb +4 -2
- data/lib/appraisal/errors.rb +8 -0
- data/lib/appraisal/file.rb +8 -2
- data/lib/appraisal/gemfile.rb +40 -10
- data/lib/appraisal/gemspec.rb +1 -7
- data/lib/appraisal/git_source.rb +23 -0
- data/lib/appraisal/group.rb +17 -0
- data/lib/appraisal/task.rb +20 -18
- data/lib/appraisal/utils.rb +8 -0
- data/lib/appraisal/version.rb +1 -1
- data/spec/acceptance/cli_spec.rb +233 -0
- data/spec/appraisal/appraisal_spec.rb +61 -7
- data/spec/appraisal/file_spec.rb +9 -0
- data/spec/appraisal/gemfile_spec.rb +43 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/support/acceptance_test_helpers.rb +113 -0
- metadata +61 -11
- data/GOALS +0 -5
@@ -0,0 +1,29 @@
|
|
1
|
+
module DependencyHelpers
|
2
|
+
def build_gem(name, version)
|
3
|
+
create_dir(name)
|
4
|
+
cd(name)
|
5
|
+
create_dir("lib")
|
6
|
+
gem_path = "#{name}.gemspec"
|
7
|
+
version_path = "lib/#{name}.rb"
|
8
|
+
spec = <<-SPEC
|
9
|
+
Gem::Specification.new do |s|
|
10
|
+
s.name = '#{name}'
|
11
|
+
s.version = '#{version}'
|
12
|
+
s.authors = 'Mr. Smith'
|
13
|
+
s.summary = 'summary'
|
14
|
+
s.files = '#{version_path}'
|
15
|
+
end
|
16
|
+
SPEC
|
17
|
+
write_file(gem_path, spec)
|
18
|
+
write_file(version_path, "$#{name}_version = '#{version}'")
|
19
|
+
in_current_dir { `gem build #{gem_path} 2>&1` }
|
20
|
+
set_env("GEM_HOME", TMP_GEM_ROOT)
|
21
|
+
in_current_dir { `gem install #{name}-#{version}.gem 2>&1` }
|
22
|
+
FileUtils.rm_rf(File.join(current_dir, name))
|
23
|
+
dirs.pop
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
if respond_to?(:World)
|
28
|
+
World(DependencyHelpers)
|
29
|
+
end
|
data/lib/appraisal/appraisal.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'appraisal/gemfile'
|
2
2
|
require 'appraisal/command'
|
3
|
+
require 'appraisal/utils'
|
3
4
|
require 'fileutils'
|
5
|
+
require 'pathname'
|
4
6
|
|
5
7
|
module Appraisal
|
6
8
|
# Represents one appraisal and its dependencies
|
@@ -18,37 +20,75 @@ module Appraisal
|
|
18
20
|
|
19
21
|
def write_gemfile
|
20
22
|
::File.open(gemfile_path, "w") do |file|
|
21
|
-
|
22
|
-
file.puts
|
23
|
-
file.write(gemfile.to_s)
|
23
|
+
signature = "# This file was generated by Appraisal"
|
24
|
+
file.puts([signature, gemfile.to_s].reject {|s| s.empty? }.join("\n\n"))
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
27
|
-
def install
|
28
|
-
Command.new(
|
28
|
+
def install(job_size = 1)
|
29
|
+
Command.new(check_command + ' || ' + install_command(job_size)).run
|
30
|
+
end
|
31
|
+
|
32
|
+
def update(gems = [])
|
33
|
+
Command.new(update_command(gems)).run
|
29
34
|
end
|
30
35
|
|
31
36
|
def gemfile_path
|
32
|
-
unless
|
33
|
-
|
37
|
+
unless gemfile_root.exist?
|
38
|
+
gemfile_root.mkdir
|
34
39
|
end
|
35
40
|
|
36
|
-
|
41
|
+
gemfile_root.join("#{clean_name}.gemfile").to_s
|
37
42
|
end
|
38
43
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
44
|
+
def relativize
|
45
|
+
current_directory = Pathname.new(Dir.pwd)
|
46
|
+
relative_path = current_directory.relative_path_from(gemfile_root).cleanpath
|
47
|
+
lockfile_content = ::File.read(lockfile_path)
|
48
|
+
|
49
|
+
::File.open(lockfile_path, 'w') do |file|
|
50
|
+
file.write lockfile_content.gsub(/#{current_directory}/, relative_path.to_s)
|
51
|
+
end
|
42
52
|
end
|
43
53
|
|
44
54
|
private
|
45
55
|
|
56
|
+
def check_command
|
57
|
+
gemfile_option = "--gemfile='#{gemfile_path}'"
|
58
|
+
['bundle', 'check', gemfile_option].join(' ')
|
59
|
+
end
|
60
|
+
|
61
|
+
def install_command(job_size)
|
62
|
+
gemfile_option = "--gemfile='#{gemfile_path}'"
|
63
|
+
['bundle', 'install', gemfile_option, bundle_parallel_option(job_size)].compact.join(' ')
|
64
|
+
end
|
65
|
+
|
66
|
+
def update_command(gems)
|
67
|
+
gemfile_config = "BUNDLE_GEMFILE='#{gemfile_path}'"
|
68
|
+
[gemfile_config, 'bundle', 'update', *gems].compact.join(' ')
|
69
|
+
end
|
70
|
+
|
46
71
|
def gemfile_root
|
47
|
-
::File.join(Dir.pwd, "gemfiles")
|
72
|
+
Pathname.new(::File.join(Dir.pwd, "gemfiles"))
|
73
|
+
end
|
74
|
+
|
75
|
+
def lockfile_path
|
76
|
+
"#{gemfile_path}.lock"
|
48
77
|
end
|
49
78
|
|
50
79
|
def clean_name
|
51
80
|
name.gsub(/[^\w\.]/, '_')
|
52
81
|
end
|
82
|
+
|
83
|
+
def bundle_parallel_option(job_size)
|
84
|
+
if job_size > 1
|
85
|
+
if Utils.support_parallel_installation?
|
86
|
+
"--jobs=#{job_size}"
|
87
|
+
else
|
88
|
+
warn 'Your current version of Bundler does not support parallel installation. Please ' +
|
89
|
+
'upgrade Bundler to version >= 1.4.0, or invoke `appraisal` without `--jobs` option.'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
53
93
|
end
|
54
94
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Appraisal
|
5
|
+
class CLI < Thor
|
6
|
+
default_task :install
|
7
|
+
|
8
|
+
# Override help command to print out usage
|
9
|
+
def self.help(shell, subcommand = false)
|
10
|
+
shell.say strip_heredoc(<<-help)
|
11
|
+
Appraisal: Find out what your Ruby gems are worth.
|
12
|
+
|
13
|
+
Usage:
|
14
|
+
appraisal [APPRAISAL_NAME] EXTERNAL_COMMAND
|
15
|
+
|
16
|
+
If APPRAISAL_NAME is given, only run that EXTERNAL_COMMAND against the given
|
17
|
+
appraisal, otherwise it runs the EXTERNAL_COMMAND against all appraisals.
|
18
|
+
|
19
|
+
Available Appraisal(s):
|
20
|
+
help
|
21
|
+
|
22
|
+
File.each do |appraisal|
|
23
|
+
shell.say " - #{appraisal.name}"
|
24
|
+
end
|
25
|
+
|
26
|
+
shell.say
|
27
|
+
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.exit_on_failure?
|
32
|
+
true
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'install', 'Resolve and install dependencies for each appraisal'
|
36
|
+
method_option 'jobs', aliases: 'j', type: :numeric, default: 1, banner: 'SIZE',
|
37
|
+
desc: 'Install gems in parallel using the given number of workers.'
|
38
|
+
def install
|
39
|
+
invoke :generate, [], {}
|
40
|
+
|
41
|
+
File.each do |appraisal|
|
42
|
+
appraisal.install(options[:jobs])
|
43
|
+
appraisal.relativize
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
desc 'generate', 'Generate a gemfile for each appraisal'
|
48
|
+
def generate
|
49
|
+
File.each do |appraisal|
|
50
|
+
appraisal.write_gemfile
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
desc 'clean', 'Remove all generated gemfiles and lockfiles from gemfiles folder'
|
55
|
+
def clean
|
56
|
+
FileUtils.rm_f Dir['gemfiles/*.{gemfile,gemfile.lock}']
|
57
|
+
end
|
58
|
+
|
59
|
+
desc 'update [LIST_OF_GEMS]', 'Remove all generated gemfiles and lockfiles, resolve, and install dependencies again'
|
60
|
+
def update(*gems)
|
61
|
+
invoke :generate, []
|
62
|
+
|
63
|
+
File.each do |appraisal|
|
64
|
+
appraisal.update(gems)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def method_missing(name, *args, &block)
|
71
|
+
matching_appraisal = File.new.appraisals.detect { |appraisal| appraisal.name == name.to_s }
|
72
|
+
|
73
|
+
if matching_appraisal
|
74
|
+
Command.new(args.join(' '), matching_appraisal.gemfile_path).run
|
75
|
+
else
|
76
|
+
File.each do |appraisal|
|
77
|
+
Command.new(ARGV.join(' '), appraisal.gemfile_path).run
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.strip_heredoc(string)
|
83
|
+
indent = string.scan(/^[ \t]*(?=\S)/).min.size || 0
|
84
|
+
string.gsub(/^[ \t]{#{indent}}/, '')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/appraisal/command.rb
CHANGED
@@ -4,14 +4,15 @@ module Appraisal
|
|
4
4
|
BUNDLER_ENV_VARS = %w(RUBYOPT BUNDLE_PATH BUNDLE_BIN_PATH BUNDLE_GEMFILE).freeze
|
5
5
|
|
6
6
|
def self.from_args(gemfile)
|
7
|
-
|
7
|
+
ARGV.shift
|
8
|
+
command = ([$0] + ARGV).join(' ')
|
8
9
|
new(command, gemfile)
|
9
10
|
end
|
10
11
|
|
11
12
|
def initialize(command, gemfile = nil)
|
12
13
|
@original_env = {}
|
13
14
|
@gemfile = gemfile
|
14
|
-
if command =~ /^bundle/
|
15
|
+
if command =~ /^(bundle|BUNDLE_GEMFILE)/
|
15
16
|
@command = command
|
16
17
|
else
|
17
18
|
@command = "bundle exec #{command}"
|
@@ -37,6 +38,7 @@ module Appraisal
|
|
37
38
|
def with_clean_env
|
38
39
|
unset_bundler_env_vars
|
39
40
|
ENV['BUNDLE_GEMFILE'] = @gemfile
|
41
|
+
ENV['APPRAISAL_INITIALIZED'] = '1'
|
40
42
|
yield
|
41
43
|
ensure
|
42
44
|
restore_env
|
data/lib/appraisal/file.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'appraisal/appraisal'
|
2
|
+
require 'appraisal/errors'
|
2
3
|
require 'appraisal/gemfile'
|
3
4
|
|
4
5
|
module Appraisal
|
5
|
-
# Loads and parses
|
6
|
+
# Loads and parses Appraisals file
|
6
7
|
class File
|
7
8
|
attr_reader :appraisals, :gemfile
|
8
9
|
|
@@ -14,7 +15,12 @@ module Appraisal
|
|
14
15
|
@appraisals = []
|
15
16
|
@gemfile = Gemfile.new
|
16
17
|
@gemfile.load(ENV['BUNDLE_GEMFILE'] || 'Gemfile')
|
17
|
-
|
18
|
+
|
19
|
+
if ::File.exists? path
|
20
|
+
run IO.read(path)
|
21
|
+
else
|
22
|
+
raise AppraisalsNotFound
|
23
|
+
end
|
18
24
|
end
|
19
25
|
|
20
26
|
def each(&block)
|
data/lib/appraisal/gemfile.rb
CHANGED
@@ -8,8 +8,11 @@ module Appraisal
|
|
8
8
|
|
9
9
|
def initialize
|
10
10
|
@sources = []
|
11
|
+
@ruby_version = nil
|
11
12
|
@dependencies = []
|
12
13
|
@gemspec = nil
|
14
|
+
@groups = []
|
15
|
+
@git_sources = []
|
13
16
|
end
|
14
17
|
|
15
18
|
def load(path)
|
@@ -17,7 +20,7 @@ module Appraisal
|
|
17
20
|
end
|
18
21
|
|
19
22
|
def run(definitions)
|
20
|
-
instance_eval(definitions, __FILE__, __LINE__)
|
23
|
+
instance_eval(definitions, __FILE__, __LINE__) if definitions
|
21
24
|
end
|
22
25
|
|
23
26
|
def gem(name, *requirements)
|
@@ -25,25 +28,38 @@ module Appraisal
|
|
25
28
|
@dependencies << Dependency.new(name, requirements)
|
26
29
|
end
|
27
30
|
|
28
|
-
def group(*names)
|
29
|
-
|
31
|
+
def group(*names, &block)
|
32
|
+
require 'appraisal/group'
|
33
|
+
|
34
|
+
group = Group.new(names)
|
35
|
+
group.run(&block)
|
36
|
+
@groups << group
|
30
37
|
end
|
31
38
|
|
32
39
|
def source(source)
|
33
40
|
@sources << source
|
34
41
|
end
|
35
42
|
|
43
|
+
def ruby(ruby_version)
|
44
|
+
@ruby_version = ruby_version
|
45
|
+
end
|
46
|
+
|
47
|
+
def git(source, options = {}, &block)
|
48
|
+
require 'appraisal/git_source'
|
49
|
+
|
50
|
+
git_source = GitSource.new(source, options)
|
51
|
+
git_source.run(&block)
|
52
|
+
@git_sources << git_source
|
53
|
+
end
|
54
|
+
|
36
55
|
def to_s
|
37
|
-
[source_entry, dependencies_entry,
|
56
|
+
[source_entry, ruby_version_entry, git_sources_entry, dependencies_entry, groups_entry,
|
57
|
+
gemspec_entry].reject{ |s| s.nil? || s.empty? }.join("\n\n").strip
|
38
58
|
end
|
39
59
|
|
40
60
|
def dup
|
41
61
|
gemfile = Gemfile.new
|
42
|
-
|
43
|
-
dependencies.each do |dependency|
|
44
|
-
gemfile.gem(dependency.name, *dependency.requirements)
|
45
|
-
end
|
46
|
-
gemfile.gemspec(@gemspec.options) if @gemspec
|
62
|
+
gemfile.run(to_s)
|
47
63
|
gemfile
|
48
64
|
end
|
49
65
|
|
@@ -57,8 +73,22 @@ module Appraisal
|
|
57
73
|
@sources.map { |source| "source #{source.inspect}" }.join("\n")
|
58
74
|
end
|
59
75
|
|
76
|
+
def ruby_version_entry
|
77
|
+
if @ruby_version
|
78
|
+
"ruby #{@ruby_version.inspect}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def git_sources_entry
|
83
|
+
@git_sources.map(&:to_s).join("\n\n")
|
84
|
+
end
|
85
|
+
|
60
86
|
def dependencies_entry
|
61
|
-
dependencies.map
|
87
|
+
dependencies.map(&:to_s).join("\n")
|
88
|
+
end
|
89
|
+
|
90
|
+
def groups_entry
|
91
|
+
@groups.map(&:to_s).join("\n\n")
|
62
92
|
end
|
63
93
|
|
64
94
|
def gemspec_entry
|
data/lib/appraisal/gemspec.rb
CHANGED
@@ -9,14 +9,8 @@ module Appraisal
|
|
9
9
|
@options[:path] ||= '.'
|
10
10
|
end
|
11
11
|
|
12
|
-
def exists?
|
13
|
-
Dir[::File.join(@options[:path], "*.gemspec")].size > 0
|
14
|
-
end
|
15
|
-
|
16
12
|
def to_s
|
17
|
-
|
18
|
-
"gemspec #{exported_options.inspect.gsub(/^\{|\}$/, '')}"
|
19
|
-
end
|
13
|
+
"gemspec #{exported_options.inspect.gsub(/^\{|\}$/, '')}"
|
20
14
|
end
|
21
15
|
|
22
16
|
private
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Appraisal
|
2
|
+
class GitSource < Gemfile
|
3
|
+
def initialize(source, options = {})
|
4
|
+
super()
|
5
|
+
@source = source
|
6
|
+
@options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
def run(&block)
|
10
|
+
instance_exec(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_s
|
14
|
+
dependencies = super.strip.gsub(/^/, ' ')
|
15
|
+
|
16
|
+
if @options.empty?
|
17
|
+
"git #{@source.inspect} do\n#{dependencies}\nend"
|
18
|
+
else
|
19
|
+
"git #{@source.inspect}, #{@options.inspect} do\n#{dependencies}\nend"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Appraisal
|
2
|
+
class Group < Gemfile
|
3
|
+
def initialize(group_names)
|
4
|
+
super()
|
5
|
+
@group_names = group_names
|
6
|
+
end
|
7
|
+
|
8
|
+
def run(&block)
|
9
|
+
instance_exec(&block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"group #{@group_names.map(&:inspect).join(', ')} do\n" +
|
14
|
+
super.strip.gsub(/^/, ' ') + "\nend"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/appraisal/task.rb
CHANGED
@@ -7,38 +7,40 @@ module Appraisal
|
|
7
7
|
class Task < Rake::TaskLib
|
8
8
|
def initialize
|
9
9
|
namespace :appraisal do
|
10
|
-
desc "Generate a Gemfile for each appraisal"
|
10
|
+
desc "DEPRECATED: Generate a Gemfile for each appraisal"
|
11
11
|
task :gemfiles do
|
12
|
-
|
13
|
-
appraisal
|
14
|
-
|
12
|
+
warn "`rake appraisal:gemfile` task is deprecated and will be removed soon. " +
|
13
|
+
"Please use `appraisal generate`."
|
14
|
+
exec 'bundle exec appraisal generate'
|
15
15
|
end
|
16
16
|
|
17
|
-
desc "Resolve and install dependencies for each appraisal"
|
18
|
-
task :install
|
19
|
-
|
20
|
-
appraisal
|
21
|
-
|
17
|
+
desc "DEPRECATED: Resolve and install dependencies for each appraisal"
|
18
|
+
task :install do
|
19
|
+
warn "`rake appraisal:install` task is deprecated and will be removed soon. " +
|
20
|
+
"Please use `appraisal install`."
|
21
|
+
exec 'bundle exec appraisal install'
|
22
22
|
end
|
23
23
|
|
24
|
-
desc "Remove all generated gemfiles from gemfiles/ folder"
|
24
|
+
desc "DEPRECATED: Remove all generated gemfiles from gemfiles/ folder"
|
25
25
|
task :cleanup do
|
26
|
-
|
27
|
-
|
26
|
+
warn "`rake appraisal:cleanup` task is deprecated and will be removed soon. " +
|
27
|
+
"Please use `appraisal clean`."
|
28
|
+
exec 'bundle exec appraisal clean'
|
28
29
|
end
|
29
30
|
|
30
31
|
File.each do |appraisal|
|
31
|
-
desc "Run the given task for appraisal #{appraisal.name}"
|
32
|
+
desc "DEPRECATED: Run the given task for appraisal #{appraisal.name}"
|
32
33
|
task appraisal.name do
|
33
|
-
|
34
|
+
ARGV.shift
|
35
|
+
warn "`rake appraisal:#{appraisal.name}` task is deprecated and will be removed soon. " +
|
36
|
+
"Please use `appraisal #{appraisal.name} rake #{ARGV.join(' ')}`."
|
37
|
+
exec "bundle exec appraisal #{appraisal.name} rake #{ARGV.join(' ')}"
|
34
38
|
end
|
35
39
|
end
|
36
40
|
|
37
41
|
task :all do
|
38
|
-
|
39
|
-
|
40
|
-
end
|
41
|
-
exit
|
42
|
+
ARGV.shift
|
43
|
+
exec "bundle exec appraisal rake #{ARGV.join(' ')}"
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|