appbundler 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +16 -16
- data/.rspec +2 -2
- data/.travis.yml +16 -12
- data/Gemfile +4 -4
- data/LICENSE.txt +202 -202
- data/README.md +70 -70
- data/Rakefile +1 -1
- data/appbundler.gemspec +27 -27
- data/bin/appbundler +13 -13
- data/lib/appbundler.rb +4 -4
- data/lib/appbundler/app.rb +194 -192
- data/lib/appbundler/cli.rb +113 -113
- data/lib/appbundler/version.rb +3 -3
- data/spec/appbundler/app_spec.rb +421 -421
- data/spec/fixtures/appbundler-example-app/.bundle/config +2 -2
- data/spec/fixtures/appbundler-example-app/Gemfile +2 -2
- data/spec/fixtures/appbundler-example-app/Gemfile.lock.unix +135 -135
- data/spec/fixtures/appbundler-example-app/Gemfile.lock.windows +172 -172
- data/spec/fixtures/appbundler-example-app/README.md +3 -3
- data/spec/fixtures/appbundler-example-app/appbundler-example-app.gemspec +22 -22
- data/spec/fixtures/appbundler-example-app/bin/app-binary-1 +2 -2
- data/spec/fixtures/appbundler-example-app/bin/app-binary-2 +2 -2
- data/spec/fixtures/appbundler-example-app/lib/example_app.rb +3 -3
- data/spec/spec_helper.rb +9 -9
- metadata +3 -3
data/README.md
CHANGED
@@ -1,70 +1,70 @@
|
|
1
|
-
# Appbundler
|
2
|
-
|
3
|
-
Appbundler reads a Gemfile.lock and generates code with
|
4
|
-
`gem "some-dep", "= VERSION"` statements to lock the app's dependencies
|
5
|
-
to the versions selected by bundler. This code is used in binstubs for
|
6
|
-
the application so that running (e.g.) `chef-client` on the command line
|
7
|
-
activates the locked dependencies for `chef` before running the command.
|
8
|
-
|
9
|
-
This provides the following benefits:
|
10
|
-
* The application loads faster because rubygems is not resolving
|
11
|
-
dependency constraints at runtime.
|
12
|
-
* The application runs with the same dependencies that it would if
|
13
|
-
bundler was used, so we can test applications (that will be installed
|
14
|
-
in an omnibus package) using the default bundler workflow.
|
15
|
-
* There's no need to `bundle exec` or patch the bundler runtime into the
|
16
|
-
app.
|
17
|
-
* The app can load gems not included in the Gemfile/gemspec. Our use
|
18
|
-
case for this is to load plugins (e.g., for knife and test kitchen).
|
19
|
-
* A user can use rvm and still use the application (see below).
|
20
|
-
* The application is protected from installation of incompatible
|
21
|
-
dependencies.
|
22
|
-
|
23
|
-
# Usage
|
24
|
-
|
25
|
-
Install via rubygems: `gem install appbundler` or clone this project and
|
26
|
-
bundle install:
|
27
|
-
|
28
|
-
```
|
29
|
-
git clone https://github.com/opscode/appbundler.git
|
30
|
-
cd appbundler
|
31
|
-
bundle install
|
32
|
-
```
|
33
|
-
|
34
|
-
Clone whatever project you want to appbundle somewhere else, and bundle
|
35
|
-
install it:
|
36
|
-
|
37
|
-
```
|
38
|
-
mkdir ~/oc
|
39
|
-
cd ~/oc
|
40
|
-
git clone https://github.com/opscode/chef.git
|
41
|
-
cd chef
|
42
|
-
bundle install
|
43
|
-
```
|
44
|
-
|
45
|
-
Create a bin directory where your bundled binstubs will live:
|
46
|
-
|
47
|
-
```
|
48
|
-
mkdir ~/appbundle-bin
|
49
|
-
# Add to your PATH if you like
|
50
|
-
```
|
51
|
-
|
52
|
-
Now you can app bundle your project (chef in our example):
|
53
|
-
|
54
|
-
```
|
55
|
-
bin/appbundler ~/oc/chef ~/appbundler-bin
|
56
|
-
```
|
57
|
-
|
58
|
-
Now you can run all of the app's executables with locked down deps:
|
59
|
-
|
60
|
-
```
|
61
|
-
~/appbunlder-bin/chef-client -v
|
62
|
-
```
|
63
|
-
|
64
|
-
|
65
|
-
# RVM
|
66
|
-
|
67
|
-
The generated binstubs explicitly disable rvm, so the above won't work
|
68
|
-
if you're using rvm. This is intentional, because our use case is for
|
69
|
-
omnibus applications where rvm's environment variables can break the
|
70
|
-
embedded application by making ruby look for gems in rvm's gem repo.
|
1
|
+
# Appbundler
|
2
|
+
|
3
|
+
Appbundler reads a Gemfile.lock and generates code with
|
4
|
+
`gem "some-dep", "= VERSION"` statements to lock the app's dependencies
|
5
|
+
to the versions selected by bundler. This code is used in binstubs for
|
6
|
+
the application so that running (e.g.) `chef-client` on the command line
|
7
|
+
activates the locked dependencies for `chef` before running the command.
|
8
|
+
|
9
|
+
This provides the following benefits:
|
10
|
+
* The application loads faster because rubygems is not resolving
|
11
|
+
dependency constraints at runtime.
|
12
|
+
* The application runs with the same dependencies that it would if
|
13
|
+
bundler was used, so we can test applications (that will be installed
|
14
|
+
in an omnibus package) using the default bundler workflow.
|
15
|
+
* There's no need to `bundle exec` or patch the bundler runtime into the
|
16
|
+
app.
|
17
|
+
* The app can load gems not included in the Gemfile/gemspec. Our use
|
18
|
+
case for this is to load plugins (e.g., for knife and test kitchen).
|
19
|
+
* A user can use rvm and still use the application (see below).
|
20
|
+
* The application is protected from installation of incompatible
|
21
|
+
dependencies.
|
22
|
+
|
23
|
+
# Usage
|
24
|
+
|
25
|
+
Install via rubygems: `gem install appbundler` or clone this project and
|
26
|
+
bundle install:
|
27
|
+
|
28
|
+
```
|
29
|
+
git clone https://github.com/opscode/appbundler.git
|
30
|
+
cd appbundler
|
31
|
+
bundle install
|
32
|
+
```
|
33
|
+
|
34
|
+
Clone whatever project you want to appbundle somewhere else, and bundle
|
35
|
+
install it:
|
36
|
+
|
37
|
+
```
|
38
|
+
mkdir ~/oc
|
39
|
+
cd ~/oc
|
40
|
+
git clone https://github.com/opscode/chef.git
|
41
|
+
cd chef
|
42
|
+
bundle install
|
43
|
+
```
|
44
|
+
|
45
|
+
Create a bin directory where your bundled binstubs will live:
|
46
|
+
|
47
|
+
```
|
48
|
+
mkdir ~/appbundle-bin
|
49
|
+
# Add to your PATH if you like
|
50
|
+
```
|
51
|
+
|
52
|
+
Now you can app bundle your project (chef in our example):
|
53
|
+
|
54
|
+
```
|
55
|
+
bin/appbundler ~/oc/chef ~/appbundler-bin
|
56
|
+
```
|
57
|
+
|
58
|
+
Now you can run all of the app's executables with locked down deps:
|
59
|
+
|
60
|
+
```
|
61
|
+
~/appbunlder-bin/chef-client -v
|
62
|
+
```
|
63
|
+
|
64
|
+
|
65
|
+
# RVM
|
66
|
+
|
67
|
+
The generated binstubs explicitly disable rvm, so the above won't work
|
68
|
+
if you're using rvm. This is intentional, because our use case is for
|
69
|
+
omnibus applications where rvm's environment variables can break the
|
70
|
+
embedded application by making ruby look for gems in rvm's gem repo.
|
data/Rakefile
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require "bundler/gem_tasks"
|
1
|
+
require "bundler/gem_tasks"
|
data/appbundler.gemspec
CHANGED
@@ -1,27 +1,27 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'appbundler/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "appbundler"
|
8
|
-
spec.version = Appbundler::VERSION
|
9
|
-
spec.authors = ["danielsdeleo"]
|
10
|
-
spec.email = ["dan@opscode.com"]
|
11
|
-
spec.description = %q{Extracts a dependency solution from bundler's Gemfile.lock to speed gem activation}
|
12
|
-
spec.summary = spec.description
|
13
|
-
spec.homepage = ""
|
14
|
-
spec.license = "Apache2"
|
15
|
-
|
16
|
-
spec.files = `git ls-files`.split($/)
|
17
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = ["lib"]
|
20
|
-
|
21
|
-
spec.add_development_dependency "rake"
|
22
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
23
|
-
spec.add_development_dependency "pry"
|
24
|
-
spec.add_development_dependency "mixlib-shellout", "~> 1.0"
|
25
|
-
|
26
|
-
spec.add_dependency "mixlib-cli", "~> 1.4"
|
27
|
-
end
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'appbundler/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "appbundler"
|
8
|
+
spec.version = Appbundler::VERSION
|
9
|
+
spec.authors = ["danielsdeleo"]
|
10
|
+
spec.email = ["dan@opscode.com"]
|
11
|
+
spec.description = %q{Extracts a dependency solution from bundler's Gemfile.lock to speed gem activation}
|
12
|
+
spec.summary = spec.description
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "Apache2"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "rake"
|
22
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
23
|
+
spec.add_development_dependency "pry"
|
24
|
+
spec.add_development_dependency "mixlib-shellout", "~> 1.0"
|
25
|
+
|
26
|
+
spec.add_dependency "mixlib-cli", "~> 1.4"
|
27
|
+
end
|
data/bin/appbundler
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
Kernel.trap(:INT) { exit 1 }
|
4
|
-
|
5
|
-
begin
|
6
|
-
require 'appbundler/cli'
|
7
|
-
rescue LoadError
|
8
|
-
$:.unshift File.expand_path("../../lib", __FILE__)
|
9
|
-
require 'appbundler/cli'
|
10
|
-
end
|
11
|
-
|
12
|
-
Appbundler::CLI.run(ARGV)
|
13
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
Kernel.trap(:INT) { exit 1 }
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'appbundler/cli'
|
7
|
+
rescue LoadError
|
8
|
+
$:.unshift File.expand_path("../../lib", __FILE__)
|
9
|
+
require 'appbundler/cli'
|
10
|
+
end
|
11
|
+
|
12
|
+
Appbundler::CLI.run(ARGV)
|
13
|
+
|
data/lib/appbundler.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
2
|
-
module Appbundler
|
3
|
-
end
|
4
|
-
|
1
|
+
|
2
|
+
module Appbundler
|
3
|
+
end
|
4
|
+
|
data/lib/appbundler/app.rb
CHANGED
@@ -1,192 +1,194 @@
|
|
1
|
-
require 'bundler'
|
2
|
-
require 'fileutils'
|
3
|
-
require 'pp'
|
4
|
-
|
5
|
-
module Appbundler
|
6
|
-
class App
|
7
|
-
|
8
|
-
BINSTUB_FILE_VERSION=1
|
9
|
-
|
10
|
-
attr_reader :app_root
|
11
|
-
attr_reader :target_bin_dir
|
12
|
-
|
13
|
-
def self.demo
|
14
|
-
demo = new("/Users/ddeleo/oc/chef")
|
15
|
-
|
16
|
-
knife = demo.executables.grep(/knife/).first
|
17
|
-
puts demo.binstub(knife)
|
18
|
-
end
|
19
|
-
|
20
|
-
def initialize(app_root, target_bin_dir)
|
21
|
-
@app_root = app_root
|
22
|
-
@target_bin_dir = target_bin_dir
|
23
|
-
end
|
24
|
-
|
25
|
-
# Copy over any .bundler and Gemfile.lock files to the target gem
|
26
|
-
# directory. This will let us run tests from under that directory.
|
27
|
-
def copy_bundler_env
|
28
|
-
gem_path = app_gemspec.gem_dir
|
29
|
-
FileUtils.install(gemfile_lock, gem_path, :mode => 0644)
|
30
|
-
if File.exist?(dot_bundle_dir) && File.directory?(dot_bundle_dir)
|
31
|
-
FileUtils.cp_r(dot_bundle_dir, gem_path)
|
32
|
-
FileUtils.chmod_R("ugo+rX", File.join(gem_path, ".bundle"))
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def write_executable_stubs
|
37
|
-
executables_to_create = executables.map do |real_executable_path|
|
38
|
-
basename = File.basename(real_executable_path)
|
39
|
-
stub_path = File.join(target_bin_dir, basename)
|
40
|
-
[real_executable_path, stub_path]
|
41
|
-
end
|
42
|
-
|
43
|
-
executables_to_create.each do |real_executable_path, stub_path|
|
44
|
-
File.open(stub_path, "wb", 0755) do |f|
|
45
|
-
f.write(binstub(real_executable_path))
|
46
|
-
end
|
47
|
-
if RUBY_PLATFORM =~ /mswin|mingw|windows/
|
48
|
-
batch_wrapper_path = "#{stub_path}.bat"
|
49
|
-
File.open(batch_wrapper_path, "wb", 0755) do |f|
|
50
|
-
f.write(batchfile_stub)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
executables_to_create
|
56
|
-
end
|
57
|
-
|
58
|
-
def name
|
59
|
-
File.basename(app_root)
|
60
|
-
end
|
61
|
-
|
62
|
-
def dot_bundle_dir
|
63
|
-
File.join(app_root, ".bundle")
|
64
|
-
end
|
65
|
-
|
66
|
-
def gemfile_lock
|
67
|
-
File.join(app_root, "Gemfile.lock")
|
68
|
-
end
|
69
|
-
|
70
|
-
def ruby
|
71
|
-
Gem.ruby
|
72
|
-
end
|
73
|
-
|
74
|
-
def batchfile_stub
|
75
|
-
ruby_relpath_windows = ruby_relative_path.gsub('/', '\\')
|
76
|
-
<<-E
|
77
|
-
@ECHO OFF
|
78
|
-
"%~dp0\\#{ruby_relpath_windows}" "%~dpn0" %*
|
79
|
-
E
|
80
|
-
end
|
81
|
-
|
82
|
-
# Relative path from #target_bin_dir to #ruby. This is used to
|
83
|
-
# generate batch files for windows in a way that the package can be
|
84
|
-
# installed in a custom location. On Unix we don't support custom
|
85
|
-
# install locations so this isn't needed.
|
86
|
-
def ruby_relative_path
|
87
|
-
ruby_pathname = Pathname.new(ruby)
|
88
|
-
bindir_pathname = Pathname.new(target_bin_dir)
|
89
|
-
ruby_pathname.relative_path_from(bindir_pathname).to_s
|
90
|
-
end
|
91
|
-
|
92
|
-
def shebang
|
93
|
-
"#!#{ruby}\n"
|
94
|
-
end
|
95
|
-
|
96
|
-
# A specially formatted comment that documents the format version of the
|
97
|
-
# binstub files we generate.
|
98
|
-
#
|
99
|
-
# This comment should be unusual enough that we can reliably (enough)
|
100
|
-
# detect whether a binstub was created by Appbundler and parse it to learn
|
101
|
-
# what version of the format it uses. If we ever need to support reading or
|
102
|
-
# mutating existing binstubs, we'll know what file version we're starting
|
103
|
-
# with.
|
104
|
-
def file_format_comment
|
105
|
-
"#--APP_BUNDLER_BINSTUB_FORMAT_VERSION=#{BINSTUB_FILE_VERSION}--\n"
|
106
|
-
end
|
107
|
-
|
108
|
-
# Ruby code (as a string) that clears GEM_HOME and GEM_PATH environment
|
109
|
-
# variables. In an omnibus context, this is important so users can use
|
110
|
-
# things like rvm without accidentally pointing the app at rvm's
|
111
|
-
# ruby and gems.
|
112
|
-
#
|
113
|
-
# Environment sanitization can be skipped by setting the
|
114
|
-
# APPBUNDLER_ALLOW_RVM environment variable to "true". This feature
|
115
|
-
# exists to make tests run correctly on travis.ci (which uses rvm).
|
116
|
-
def env_sanitizer
|
117
|
-
%Q{ENV["GEM_HOME"] = ENV["GEM_PATH"] = nil unless ENV["APPBUNDLER_ALLOW_RVM"] == "true"}
|
118
|
-
end
|
119
|
-
|
120
|
-
def runtime_activate
|
121
|
-
@runtime_activate ||= begin
|
122
|
-
statements = runtime_dep_specs.map {|s| %Q|gem "#{s.name}", "= #{s.version}"|}
|
123
|
-
activate_code = ""
|
124
|
-
activate_code << env_sanitizer << "\n"
|
125
|
-
activate_code << statements.join("\n") << "\n"
|
126
|
-
activate_code
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def binstub(bin_file)
|
131
|
-
shebang + file_format_comment + runtime_activate + load_statement_for(bin_file)
|
132
|
-
end
|
133
|
-
|
134
|
-
def load_statement_for(bin_file)
|
135
|
-
name, version = app_spec.name, app_spec.version
|
136
|
-
bin_basename = File.basename(bin_file)
|
137
|
-
<<-E
|
138
|
-
gem "#{name}", "= #{version}"
|
139
|
-
|
140
|
-
spec = Gem::Specification.find_by_name("#{name}", "= #{version}")
|
141
|
-
bin_file = spec.bin_file("#{bin_basename}")
|
142
|
-
|
143
|
-
Kernel.load(bin_file)
|
144
|
-
E
|
145
|
-
end
|
146
|
-
|
147
|
-
def executables
|
148
|
-
spec = app_gemspec
|
149
|
-
spec.executables.map {|e| spec.bin_file(e)}
|
150
|
-
end
|
151
|
-
|
152
|
-
def runtime_dep_specs
|
153
|
-
add_dependencies_from(app_spec)
|
154
|
-
end
|
155
|
-
|
156
|
-
def app_dependency_names
|
157
|
-
@app_dependency_names ||= app_spec.dependencies.map(&:name)
|
158
|
-
end
|
159
|
-
|
160
|
-
def app_gemspec
|
161
|
-
Gem::Specification.find_by_name(app_spec.name, app_spec.version)
|
162
|
-
end
|
163
|
-
|
164
|
-
def app_spec
|
165
|
-
spec_for(name)
|
166
|
-
end
|
167
|
-
|
168
|
-
def gemfile_lock_specs
|
169
|
-
parsed_gemfile_lock.specs
|
170
|
-
end
|
171
|
-
|
172
|
-
def parsed_gemfile_lock
|
173
|
-
@parsed_gemfile_lock ||= Bundler::LockfileParser.new(IO.read(gemfile_lock))
|
174
|
-
end
|
175
|
-
|
176
|
-
private
|
177
|
-
|
178
|
-
def add_dependencies_from(spec, collected_deps=[])
|
179
|
-
spec.dependencies.each do |dep|
|
180
|
-
next if collected_deps.any? {|s| s.name == dep.name }
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
end
|
1
|
+
require 'bundler'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
module Appbundler
|
6
|
+
class App
|
7
|
+
|
8
|
+
BINSTUB_FILE_VERSION=1
|
9
|
+
|
10
|
+
attr_reader :app_root
|
11
|
+
attr_reader :target_bin_dir
|
12
|
+
|
13
|
+
def self.demo
|
14
|
+
demo = new("/Users/ddeleo/oc/chef")
|
15
|
+
|
16
|
+
knife = demo.executables.grep(/knife/).first
|
17
|
+
puts demo.binstub(knife)
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(app_root, target_bin_dir)
|
21
|
+
@app_root = app_root
|
22
|
+
@target_bin_dir = target_bin_dir
|
23
|
+
end
|
24
|
+
|
25
|
+
# Copy over any .bundler and Gemfile.lock files to the target gem
|
26
|
+
# directory. This will let us run tests from under that directory.
|
27
|
+
def copy_bundler_env
|
28
|
+
gem_path = app_gemspec.gem_dir
|
29
|
+
FileUtils.install(gemfile_lock, gem_path, :mode => 0644)
|
30
|
+
if File.exist?(dot_bundle_dir) && File.directory?(dot_bundle_dir)
|
31
|
+
FileUtils.cp_r(dot_bundle_dir, gem_path)
|
32
|
+
FileUtils.chmod_R("ugo+rX", File.join(gem_path, ".bundle"))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def write_executable_stubs
|
37
|
+
executables_to_create = executables.map do |real_executable_path|
|
38
|
+
basename = File.basename(real_executable_path)
|
39
|
+
stub_path = File.join(target_bin_dir, basename)
|
40
|
+
[real_executable_path, stub_path]
|
41
|
+
end
|
42
|
+
|
43
|
+
executables_to_create.each do |real_executable_path, stub_path|
|
44
|
+
File.open(stub_path, "wb", 0755) do |f|
|
45
|
+
f.write(binstub(real_executable_path))
|
46
|
+
end
|
47
|
+
if RUBY_PLATFORM =~ /mswin|mingw|windows/
|
48
|
+
batch_wrapper_path = "#{stub_path}.bat"
|
49
|
+
File.open(batch_wrapper_path, "wb", 0755) do |f|
|
50
|
+
f.write(batchfile_stub)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
executables_to_create
|
56
|
+
end
|
57
|
+
|
58
|
+
def name
|
59
|
+
File.basename(app_root)
|
60
|
+
end
|
61
|
+
|
62
|
+
def dot_bundle_dir
|
63
|
+
File.join(app_root, ".bundle")
|
64
|
+
end
|
65
|
+
|
66
|
+
def gemfile_lock
|
67
|
+
File.join(app_root, "Gemfile.lock")
|
68
|
+
end
|
69
|
+
|
70
|
+
def ruby
|
71
|
+
Gem.ruby
|
72
|
+
end
|
73
|
+
|
74
|
+
def batchfile_stub
|
75
|
+
ruby_relpath_windows = ruby_relative_path.gsub('/', '\\')
|
76
|
+
<<-E
|
77
|
+
@ECHO OFF
|
78
|
+
"%~dp0\\#{ruby_relpath_windows}" "%~dpn0" %*
|
79
|
+
E
|
80
|
+
end
|
81
|
+
|
82
|
+
# Relative path from #target_bin_dir to #ruby. This is used to
|
83
|
+
# generate batch files for windows in a way that the package can be
|
84
|
+
# installed in a custom location. On Unix we don't support custom
|
85
|
+
# install locations so this isn't needed.
|
86
|
+
def ruby_relative_path
|
87
|
+
ruby_pathname = Pathname.new(ruby)
|
88
|
+
bindir_pathname = Pathname.new(target_bin_dir)
|
89
|
+
ruby_pathname.relative_path_from(bindir_pathname).to_s
|
90
|
+
end
|
91
|
+
|
92
|
+
def shebang
|
93
|
+
"#!#{ruby}\n"
|
94
|
+
end
|
95
|
+
|
96
|
+
# A specially formatted comment that documents the format version of the
|
97
|
+
# binstub files we generate.
|
98
|
+
#
|
99
|
+
# This comment should be unusual enough that we can reliably (enough)
|
100
|
+
# detect whether a binstub was created by Appbundler and parse it to learn
|
101
|
+
# what version of the format it uses. If we ever need to support reading or
|
102
|
+
# mutating existing binstubs, we'll know what file version we're starting
|
103
|
+
# with.
|
104
|
+
def file_format_comment
|
105
|
+
"#--APP_BUNDLER_BINSTUB_FORMAT_VERSION=#{BINSTUB_FILE_VERSION}--\n"
|
106
|
+
end
|
107
|
+
|
108
|
+
# Ruby code (as a string) that clears GEM_HOME and GEM_PATH environment
|
109
|
+
# variables. In an omnibus context, this is important so users can use
|
110
|
+
# things like rvm without accidentally pointing the app at rvm's
|
111
|
+
# ruby and gems.
|
112
|
+
#
|
113
|
+
# Environment sanitization can be skipped by setting the
|
114
|
+
# APPBUNDLER_ALLOW_RVM environment variable to "true". This feature
|
115
|
+
# exists to make tests run correctly on travis.ci (which uses rvm).
|
116
|
+
def env_sanitizer
|
117
|
+
%Q{ENV["GEM_HOME"] = ENV["GEM_PATH"] = nil unless ENV["APPBUNDLER_ALLOW_RVM"] == "true"}
|
118
|
+
end
|
119
|
+
|
120
|
+
def runtime_activate
|
121
|
+
@runtime_activate ||= begin
|
122
|
+
statements = runtime_dep_specs.map {|s| %Q|gem "#{s.name}", "= #{s.version}"|}
|
123
|
+
activate_code = ""
|
124
|
+
activate_code << env_sanitizer << "\n"
|
125
|
+
activate_code << statements.join("\n") << "\n"
|
126
|
+
activate_code
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def binstub(bin_file)
|
131
|
+
shebang + file_format_comment + runtime_activate + load_statement_for(bin_file)
|
132
|
+
end
|
133
|
+
|
134
|
+
def load_statement_for(bin_file)
|
135
|
+
name, version = app_spec.name, app_spec.version
|
136
|
+
bin_basename = File.basename(bin_file)
|
137
|
+
<<-E
|
138
|
+
gem "#{name}", "= #{version}"
|
139
|
+
|
140
|
+
spec = Gem::Specification.find_by_name("#{name}", "= #{version}")
|
141
|
+
bin_file = spec.bin_file("#{bin_basename}")
|
142
|
+
|
143
|
+
Kernel.load(bin_file)
|
144
|
+
E
|
145
|
+
end
|
146
|
+
|
147
|
+
def executables
|
148
|
+
spec = app_gemspec
|
149
|
+
spec.executables.map {|e| spec.bin_file(e)}
|
150
|
+
end
|
151
|
+
|
152
|
+
def runtime_dep_specs
|
153
|
+
add_dependencies_from(app_spec)
|
154
|
+
end
|
155
|
+
|
156
|
+
def app_dependency_names
|
157
|
+
@app_dependency_names ||= app_spec.dependencies.map(&:name)
|
158
|
+
end
|
159
|
+
|
160
|
+
def app_gemspec
|
161
|
+
Gem::Specification.find_by_name(app_spec.name, app_spec.version)
|
162
|
+
end
|
163
|
+
|
164
|
+
def app_spec
|
165
|
+
spec_for(name)
|
166
|
+
end
|
167
|
+
|
168
|
+
def gemfile_lock_specs
|
169
|
+
parsed_gemfile_lock.specs
|
170
|
+
end
|
171
|
+
|
172
|
+
def parsed_gemfile_lock
|
173
|
+
@parsed_gemfile_lock ||= Bundler::LockfileParser.new(IO.read(gemfile_lock))
|
174
|
+
end
|
175
|
+
|
176
|
+
private
|
177
|
+
|
178
|
+
def add_dependencies_from(spec, collected_deps=[])
|
179
|
+
spec.dependencies.each do |dep|
|
180
|
+
next if collected_deps.any? {|s| s.name == dep.name }
|
181
|
+
# a bundler dep will not get pinned in Gemfile.lock
|
182
|
+
next if dep.name == "bundler"
|
183
|
+
next_spec = spec_for(dep.name)
|
184
|
+
collected_deps << next_spec
|
185
|
+
add_dependencies_from(next_spec, collected_deps)
|
186
|
+
end
|
187
|
+
collected_deps
|
188
|
+
end
|
189
|
+
|
190
|
+
def spec_for(dep_name)
|
191
|
+
gemfile_lock_specs.find {|s| s.name == dep_name } or raise "No spec #{dep_name}"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|