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.
@@ -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