lib/appbundler/app.rb in appbundler-0.6.0 vs lib/appbundler/app.rb in appbundler-0.7.0
- old
+ new
@@ -1,192 +1,194 @@
-require 'bundler'
-require 'fileutils'
-require 'pp'
-
-module Appbundler
- class App
-
- BINSTUB_FILE_VERSION=1
-
- attr_reader :app_root
- attr_reader :target_bin_dir
-
- def self.demo
- demo = new("/Users/ddeleo/oc/chef")
-
- knife = demo.executables.grep(/knife/).first
- puts demo.binstub(knife)
- end
-
- def initialize(app_root, target_bin_dir)
- @app_root = app_root
- @target_bin_dir = target_bin_dir
- end
-
- # Copy over any .bundler and Gemfile.lock files to the target gem
- # directory. This will let us run tests from under that directory.
- def copy_bundler_env
- gem_path = app_gemspec.gem_dir
- FileUtils.install(gemfile_lock, gem_path, :mode => 0644)
- if File.exist?(dot_bundle_dir) && File.directory?(dot_bundle_dir)
- FileUtils.cp_r(dot_bundle_dir, gem_path)
- FileUtils.chmod_R("ugo+rX", File.join(gem_path, ".bundle"))
- end
- end
-
- def write_executable_stubs
- executables_to_create = executables.map do |real_executable_path|
- basename = File.basename(real_executable_path)
- stub_path = File.join(target_bin_dir, basename)
- [real_executable_path, stub_path]
- end
-
- executables_to_create.each do |real_executable_path, stub_path|
- File.open(stub_path, "wb", 0755) do |f|
- f.write(binstub(real_executable_path))
- end
- if RUBY_PLATFORM =~ /mswin|mingw|windows/
- batch_wrapper_path = "#{stub_path}.bat"
- File.open(batch_wrapper_path, "wb", 0755) do |f|
- f.write(batchfile_stub)
- end
- end
- end
-
- executables_to_create
- end
-
- def name
- File.basename(app_root)
- end
-
- def dot_bundle_dir
- File.join(app_root, ".bundle")
- end
-
- def gemfile_lock
- File.join(app_root, "Gemfile.lock")
- end
-
- def ruby
- Gem.ruby
- end
-
- def batchfile_stub
- ruby_relpath_windows = ruby_relative_path.gsub('/', '\\')
- <<-E
-@ECHO OFF
-"%~dp0\\#{ruby_relpath_windows}" "%~dpn0" %*
-E
- end
-
- # Relative path from #target_bin_dir to #ruby. This is used to
- # generate batch files for windows in a way that the package can be
- # installed in a custom location. On Unix we don't support custom
- # install locations so this isn't needed.
- def ruby_relative_path
- ruby_pathname = Pathname.new(ruby)
- bindir_pathname = Pathname.new(target_bin_dir)
- ruby_pathname.relative_path_from(bindir_pathname).to_s
- end
-
- def shebang
- "#!#{ruby}\n"
- end
-
- # A specially formatted comment that documents the format version of the
- # binstub files we generate.
- #
- # This comment should be unusual enough that we can reliably (enough)
- # detect whether a binstub was created by Appbundler and parse it to learn
- # what version of the format it uses. If we ever need to support reading or
- # mutating existing binstubs, we'll know what file version we're starting
- # with.
- def file_format_comment
- "#--APP_BUNDLER_BINSTUB_FORMAT_VERSION=#{BINSTUB_FILE_VERSION}--\n"
- end
-
- # Ruby code (as a string) that clears GEM_HOME and GEM_PATH environment
- # variables. In an omnibus context, this is important so users can use
- # things like rvm without accidentally pointing the app at rvm's
- # ruby and gems.
- #
- # Environment sanitization can be skipped by setting the
- # APPBUNDLER_ALLOW_RVM environment variable to "true". This feature
- # exists to make tests run correctly on travis.ci (which uses rvm).
- def env_sanitizer
- %Q{ENV["GEM_HOME"] = ENV["GEM_PATH"] = nil unless ENV["APPBUNDLER_ALLOW_RVM"] == "true"}
- end
-
- def runtime_activate
- @runtime_activate ||= begin
- statements = runtime_dep_specs.map {|s| %Q|gem "#{s.name}", "= #{s.version}"|}
- activate_code = ""
- activate_code << env_sanitizer << "\n"
- activate_code << statements.join("\n") << "\n"
- activate_code
- end
- end
-
- def binstub(bin_file)
- shebang + file_format_comment + runtime_activate + load_statement_for(bin_file)
- end
-
- def load_statement_for(bin_file)
- name, version = app_spec.name, app_spec.version
- bin_basename = File.basename(bin_file)
- <<-E
-gem "#{name}", "= #{version}"
-
-spec = Gem::Specification.find_by_name("#{name}", "= #{version}")
-bin_file = spec.bin_file("#{bin_basename}")
-
-Kernel.load(bin_file)
-E
- end
-
- def executables
- spec = app_gemspec
- spec.executables.map {|e| spec.bin_file(e)}
- end
-
- def runtime_dep_specs
- add_dependencies_from(app_spec)
- end
-
- def app_dependency_names
- @app_dependency_names ||= app_spec.dependencies.map(&:name)
- end
-
- def app_gemspec
- Gem::Specification.find_by_name(app_spec.name, app_spec.version)
- end
-
- def app_spec
- spec_for(name)
- end
-
- def gemfile_lock_specs
- parsed_gemfile_lock.specs
- end
-
- def parsed_gemfile_lock
- @parsed_gemfile_lock ||= Bundler::LockfileParser.new(IO.read(gemfile_lock))
- end
-
- private
-
- def add_dependencies_from(spec, collected_deps=[])
- spec.dependencies.each do |dep|
- next if collected_deps.any? {|s| s.name == dep.name }
- next_spec = spec_for(dep.name)
- collected_deps << next_spec
- add_dependencies_from(next_spec, collected_deps)
- end
- collected_deps
- end
-
- def spec_for(dep_name)
- gemfile_lock_specs.find {|s| s.name == dep_name } or raise "No spec #{dep_name}"
- end
- end
-end
+require 'bundler'
+require 'fileutils'
+require 'pp'
+
+module Appbundler
+ class App
+
+ BINSTUB_FILE_VERSION=1
+
+ attr_reader :app_root
+ attr_reader :target_bin_dir
+
+ def self.demo
+ demo = new("/Users/ddeleo/oc/chef")
+
+ knife = demo.executables.grep(/knife/).first
+ puts demo.binstub(knife)
+ end
+
+ def initialize(app_root, target_bin_dir)
+ @app_root = app_root
+ @target_bin_dir = target_bin_dir
+ end
+
+ # Copy over any .bundler and Gemfile.lock files to the target gem
+ # directory. This will let us run tests from under that directory.
+ def copy_bundler_env
+ gem_path = app_gemspec.gem_dir
+ FileUtils.install(gemfile_lock, gem_path, :mode => 0644)
+ if File.exist?(dot_bundle_dir) && File.directory?(dot_bundle_dir)
+ FileUtils.cp_r(dot_bundle_dir, gem_path)
+ FileUtils.chmod_R("ugo+rX", File.join(gem_path, ".bundle"))
+ end
+ end
+
+ def write_executable_stubs
+ executables_to_create = executables.map do |real_executable_path|
+ basename = File.basename(real_executable_path)
+ stub_path = File.join(target_bin_dir, basename)
+ [real_executable_path, stub_path]
+ end
+
+ executables_to_create.each do |real_executable_path, stub_path|
+ File.open(stub_path, "wb", 0755) do |f|
+ f.write(binstub(real_executable_path))
+ end
+ if RUBY_PLATFORM =~ /mswin|mingw|windows/
+ batch_wrapper_path = "#{stub_path}.bat"
+ File.open(batch_wrapper_path, "wb", 0755) do |f|
+ f.write(batchfile_stub)
+ end
+ end
+ end
+
+ executables_to_create
+ end
+
+ def name
+ File.basename(app_root)
+ end
+
+ def dot_bundle_dir
+ File.join(app_root, ".bundle")
+ end
+
+ def gemfile_lock
+ File.join(app_root, "Gemfile.lock")
+ end
+
+ def ruby
+ Gem.ruby
+ end
+
+ def batchfile_stub
+ ruby_relpath_windows = ruby_relative_path.gsub('/', '\\')
+ <<-E
+@ECHO OFF
+"%~dp0\\#{ruby_relpath_windows}" "%~dpn0" %*
+E
+ end
+
+ # Relative path from #target_bin_dir to #ruby. This is used to
+ # generate batch files for windows in a way that the package can be
+ # installed in a custom location. On Unix we don't support custom
+ # install locations so this isn't needed.
+ def ruby_relative_path
+ ruby_pathname = Pathname.new(ruby)
+ bindir_pathname = Pathname.new(target_bin_dir)
+ ruby_pathname.relative_path_from(bindir_pathname).to_s
+ end
+
+ def shebang
+ "#!#{ruby}\n"
+ end
+
+ # A specially formatted comment that documents the format version of the
+ # binstub files we generate.
+ #
+ # This comment should be unusual enough that we can reliably (enough)
+ # detect whether a binstub was created by Appbundler and parse it to learn
+ # what version of the format it uses. If we ever need to support reading or
+ # mutating existing binstubs, we'll know what file version we're starting
+ # with.
+ def file_format_comment
+ "#--APP_BUNDLER_BINSTUB_FORMAT_VERSION=#{BINSTUB_FILE_VERSION}--\n"
+ end
+
+ # Ruby code (as a string) that clears GEM_HOME and GEM_PATH environment
+ # variables. In an omnibus context, this is important so users can use
+ # things like rvm without accidentally pointing the app at rvm's
+ # ruby and gems.
+ #
+ # Environment sanitization can be skipped by setting the
+ # APPBUNDLER_ALLOW_RVM environment variable to "true". This feature
+ # exists to make tests run correctly on travis.ci (which uses rvm).
+ def env_sanitizer
+ %Q{ENV["GEM_HOME"] = ENV["GEM_PATH"] = nil unless ENV["APPBUNDLER_ALLOW_RVM"] == "true"}
+ end
+
+ def runtime_activate
+ @runtime_activate ||= begin
+ statements = runtime_dep_specs.map {|s| %Q|gem "#{s.name}", "= #{s.version}"|}
+ activate_code = ""
+ activate_code << env_sanitizer << "\n"
+ activate_code << statements.join("\n") << "\n"
+ activate_code
+ end
+ end
+
+ def binstub(bin_file)
+ shebang + file_format_comment + runtime_activate + load_statement_for(bin_file)
+ end
+
+ def load_statement_for(bin_file)
+ name, version = app_spec.name, app_spec.version
+ bin_basename = File.basename(bin_file)
+ <<-E
+gem "#{name}", "= #{version}"
+
+spec = Gem::Specification.find_by_name("#{name}", "= #{version}")
+bin_file = spec.bin_file("#{bin_basename}")
+
+Kernel.load(bin_file)
+E
+ end
+
+ def executables
+ spec = app_gemspec
+ spec.executables.map {|e| spec.bin_file(e)}
+ end
+
+ def runtime_dep_specs
+ add_dependencies_from(app_spec)
+ end
+
+ def app_dependency_names
+ @app_dependency_names ||= app_spec.dependencies.map(&:name)
+ end
+
+ def app_gemspec
+ Gem::Specification.find_by_name(app_spec.name, app_spec.version)
+ end
+
+ def app_spec
+ spec_for(name)
+ end
+
+ def gemfile_lock_specs
+ parsed_gemfile_lock.specs
+ end
+
+ def parsed_gemfile_lock
+ @parsed_gemfile_lock ||= Bundler::LockfileParser.new(IO.read(gemfile_lock))
+ end
+
+ private
+
+ def add_dependencies_from(spec, collected_deps=[])
+ spec.dependencies.each do |dep|
+ next if collected_deps.any? {|s| s.name == dep.name }
+ # a bundler dep will not get pinned in Gemfile.lock
+ next if dep.name == "bundler"
+ next_spec = spec_for(dep.name)
+ collected_deps << next_spec
+ add_dependencies_from(next_spec, collected_deps)
+ end
+ collected_deps
+ end
+
+ def spec_for(dep_name)
+ gemfile_lock_specs.find {|s| s.name == dep_name } or raise "No spec #{dep_name}"
+ end
+ end
+end