appraisal 0.5.2 → 1.0.0.beta1

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.
@@ -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
@@ -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
- file.puts("# This file was generated by Appraisal")
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(bundle_command).run
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 ::File.exist?(gemfile_root)
33
- FileUtils.mkdir(gemfile_root)
37
+ unless gemfile_root.exist?
38
+ gemfile_root.mkdir
34
39
  end
35
40
 
36
- ::File.join(gemfile_root, "#{clean_name}.gemfile")
41
+ gemfile_root.join("#{clean_name}.gemfile").to_s
37
42
  end
38
43
 
39
- def bundle_command
40
- gemfile = "--gemfile='#{gemfile_path}'"
41
- "bundle check #{gemfile} || bundle install #{gemfile}"
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
@@ -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
- command = ([$0] + ARGV.slice(1, ARGV.size)).join(' ')
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
@@ -0,0 +1,8 @@
1
+ module Appraisal
2
+ # Raises when Appraisal is unable to locate Appraisals file in the current directory.
3
+ class AppraisalsNotFound < StandardError
4
+ def message
5
+ "Unable to locate 'Appraisals' file in the current directory."
6
+ end
7
+ end
8
+ end
@@ -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 Appraisal files
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
- run(IO.read(path)) if ::File.exists?(path)
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)
@@ -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
- # ignore the group
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, gemspec_entry].join("\n\n")
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
- @sources.each { |source| gemfile.source(source) }
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 { |dependency| dependency.to_s }.join("\n")
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
@@ -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
- if exists?
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
@@ -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
- File.each do |appraisal|
13
- appraisal.write_gemfile
14
- end
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 => :gemfiles do
19
- File.each do |appraisal|
20
- appraisal.install
21
- end
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
- require 'fileutils'
27
- FileUtils.rm_f Dir['gemfiles/*.{gemfile,gemfile.lock}']
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
- Command.from_args(appraisal.gemfile_path).exec
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
- File.each do |appraisal|
39
- Command.from_args(appraisal.gemfile_path).run
40
- end
41
- exit
42
+ ARGV.shift
43
+ exec "bundle exec appraisal rake #{ARGV.join(' ')}"
42
44
  end
43
45
  end
44
46