├── spec
├── rails4_spec.rb
├── bugs_spec.rb
├── no_lockfile_spec.rb
├── rails23_spec.rb
├── rails3_spec.rb
├── spec_helper.rb
└── rubies_spec.rb
├── vendor
├── plugins
│ └── taglib
│ │ └── hi
└── syck_hack.rb
├── .idea
├── .name
├── scopes
│ └── scope_settings.xml
├── encodings.xml
├── vcs.xml
├── misc.xml
├── modules.xml
├── heroku-buildpack-ruby.iml
└── .rakeTasks
├── .gitignore
├── Gemfile
├── bin
├── release
├── detect
└── compile
├── lib
├── language_pack
│ ├── no_lockfile.rb
│ ├── disable_deploys.rb
│ ├── bundler_lockfile.rb
│ ├── rack.rb
│ ├── shell_helpers.rb
│ ├── rails4.rb
│ ├── rails2.rb
│ ├── rails3.rb
│ ├── base.rb
│ └── ruby.rb
└── language_pack.rb
├── hatchet.json
├── LICENSE
├── Gemfile.lock
├── support
└── s3
│ ├── hmac
│ └── s3
├── README.md
├── CHANGELOG.md
└── Rakefile
/spec/rails4_spec.rb:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/vendor/plugins/taglib/hi:
--------------------------------------------------------------------------------
1 | hi
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | heroku-buildpack-ruby
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | repos/*
2 | .DS_Store
3 | vendor/bundler/*
4 | vendor/bundle/*
5 |
--------------------------------------------------------------------------------
/.idea/scopes/scope_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "http://rubygems.org"
2 |
3 | group :development do
4 | gem "heroku_hatchet"
5 | gem "rspec-core"
6 | gem "rspec-expectations"
7 | gem "excon"
8 | gem "rake"
9 | gem "heroku"
10 | end
11 |
--------------------------------------------------------------------------------
/bin/release:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | $:.unshift File.expand_path("../../lib", __FILE__)
4 | require "language_pack"
5 |
6 | if pack = LanguagePack.detect(ARGV[0], ARGV[1])
7 | puts pack.release
8 | end
9 |
10 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/bin/detect:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | $:.unshift File.expand_path("../../lib", __FILE__)
4 | require "language_pack"
5 |
6 | if pack = LanguagePack.detect(ARGV.first)
7 | puts pack.name
8 | exit 0
9 | else
10 | puts "no"
11 | exit 1
12 | end
13 |
--------------------------------------------------------------------------------
/bin/compile:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # sync output
4 | $stdout.sync = true
5 |
6 | $:.unshift File.expand_path("../../lib", __FILE__)
7 | require "language_pack"
8 |
9 | if pack = LanguagePack.detect(ARGV[0], ARGV[1])
10 | pack.log("compile") do
11 | pack.compile
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/spec/bugs_spec.rb:
--------------------------------------------------------------------------------
1 | require_relative 'spec_helper'
2 |
3 | describe "Bugs" do
4 | context "MRI 1.8.7" do
5 | it "should install nokogiri" do
6 | Hatchet::AnvilApp.new("mri_187_nokogiri", :buildpack => buildpack).deploy do |app, heroku, output|
7 | expect(app).to be_deployed
8 | end
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/spec/no_lockfile_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper'
2 |
3 | describe "No Lockfile" do
4 | it "should not deploy" do
5 | Hatchet::AnvilApp.new("no_lockfile", :buildpack => buildpack).deploy do |app, heroku, output|
6 | expect(app).not_to be_deployed
7 | expect(output).to include("ERROR: Gemfile.lock required")
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/spec/rails23_spec.rb:
--------------------------------------------------------------------------------
1 | require_relative 'spec_helper'
2 |
3 | describe "Rails 2.3.x" do
4 | it "should deploy on ruby 1.8.7" do
5 | Hatchet::AnvilApp.new("rails23_mri_187", :buildpack => buildpack).deploy do |app, heroku|
6 | add_database(app, heroku)
7 | expect(app).to be_deployed
8 | expect(successful_body(app)).to eq("hello")
9 | end
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/lib/language_pack/no_lockfile.rb:
--------------------------------------------------------------------------------
1 | require "language_pack"
2 | require "language_pack/base"
3 |
4 | class LanguagePack::NoLockfile < LanguagePack::Base
5 | def self.use?
6 | File.exist?("Gemfile") && !File.exists?("Gemfile.lock")
7 | end
8 |
9 | def name
10 | "Ruby/NoLockfile"
11 | end
12 |
13 | def compile
14 | error "Gemfile.lock required. Please check it in."
15 | end
16 | end
17 |
--------------------------------------------------------------------------------
/hatchet.json:
--------------------------------------------------------------------------------
1 | {
2 | "bundler": [
3 | "sharpstone/git_gemspec",
4 | "sharpstone/no_lockfile"
5 | ],
6 | "ruby": [
7 | "sharpstone/mri_187"
8 | ],
9 | "rack": [
10 | "sharpstone/mri_187_nokogiri",
11 | "sharpstone/mri_192",
12 | "sharpstone/mri_193",
13 | "sharpstone/mri_200"
14 | ],
15 | "rails2": [
16 | "sharpstone/rails23_mri_187"
17 | ],
18 | "rails3": [
19 | "sharpstone/rails3_mri_193",
20 | "sharpstone/railties3_mri_193"
21 | ]
22 | }
23 |
--------------------------------------------------------------------------------
/lib/language_pack/disable_deploys.rb:
--------------------------------------------------------------------------------
1 | require "language_pack"
2 | require "language_pack/base"
3 |
4 | class LanguagePack::DisableDeploys < LanguagePack::Base
5 | def self.use?
6 | File.exist?("Gemfile")
7 | end
8 |
9 | def name
10 | "Ruby/DisableDeploys"
11 | end
12 |
13 | def compile
14 | error "Ruby deploys have been temporarily disabled due to a Rubygems.org security breach.\nPlease see https://status.heroku.com/incidents/489 for more info and a workaround if you need to deploy."
15 | end
16 | end
17 |
18 |
--------------------------------------------------------------------------------
/lib/language_pack/bundler_lockfile.rb:
--------------------------------------------------------------------------------
1 | module LanguagePack
2 | module BundlerLockfile
3 | # checks if the Gemfile and Gemfile.lock exist
4 | def gemfile_lock?
5 | File.exist?('Gemfile') && File.exist?('Gemfile.lock')
6 | end
7 |
8 | # bootstraps bundler so we can use it before bundler is setup properlyLanguagePack::Ruby
9 | def bootstrap_bundler(&block)
10 | Dir.mktmpdir("bundler-") do |tmpdir|
11 | Dir.chdir(tmpdir) do
12 | system("curl #{LanguagePack::Base::VENDOR_URL}/#{LanguagePack::Ruby::BUNDLER_GEM_PATH}.tgz -s -o - | tar xzf -")
13 | end
14 |
15 | yield tmpdir
16 | end
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/lib/language_pack.rb:
--------------------------------------------------------------------------------
1 | require "pathname"
2 |
3 | # General Language Pack module
4 | module LanguagePack
5 |
6 | # detects which language pack to use
7 | # @param [Array] first argument is a String of the build directory
8 | # @return [LanguagePack] the {LanguagePack} detected
9 | def self.detect(*args)
10 | Dir.chdir(args.first)
11 |
12 | pack = [ NoLockfile, Rails4, Rails3, Rails2, Rack, Ruby ].detect do |klass|
13 | klass.use?
14 | end
15 |
16 | pack ? pack.new(*args) : nil
17 | end
18 |
19 | end
20 |
21 | require "language_pack/ruby"
22 | require "language_pack/rack"
23 | require "language_pack/rails2"
24 | require "language_pack/rails3"
25 | require "language_pack/disable_deploys"
26 | require "language_pack/rails4"
27 | require "language_pack/no_lockfile"
28 |
--------------------------------------------------------------------------------
/spec/rails3_spec.rb:
--------------------------------------------------------------------------------
1 | require_relative 'spec_helper'
2 |
3 | describe "Rails 3.x" do
4 | it "should deploy on ruby 1.9.3" do
5 | Hatchet::AnvilApp.new("rails3_mri_193", :buildpack => buildpack).deploy do |app, heroku|
6 | add_database(app, heroku)
7 | expect(app).to be_deployed
8 | expect(successful_body(app)).to eq("hello")
9 | end
10 | end
11 |
12 | context "when not using the rails gem" do
13 | it "should deploy on ruby 1.9.3" do
14 | Hatchet::AnvilApp.new("railties3_mri_193", :buildpack => buildpack).deploy do |app, heroku, output|
15 | add_database(app, heroku)
16 | expect(app).to be_deployed
17 | expect(output).to match("Ruby/Rails")
18 | expect(successful_body(app)).to eq("hello")
19 | end
20 | end
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'rspec/core'
2 | require 'hatchet'
3 | require 'fileutils'
4 | require 'hatchet'
5 |
6 | ENV['RACK_ENV'] = 'test'
7 |
8 | RSpec.configure do |config|
9 | config.filter_run :focused => true
10 | config.run_all_when_everything_filtered = true
11 | config.alias_example_to :fit, :focused => true
12 |
13 | config.expect_with :rspec do |c|
14 | c.syntax = :expect
15 | end
16 | config.mock_with :none
17 | end
18 |
19 | def buildpack
20 | File.expand_path(File.dirname(__FILE__) + "/..")
21 | end
22 |
23 | def git_repo
24 | "https://github.com/heroku/heroku-buildpack-ruby.git"
25 | end
26 |
27 | def add_database(app, heroku)
28 | heroku.post_addon(app.name, 'heroku-postgresql:dev')
29 | _, value = heroku.get_config_vars(app.name).body.detect {|key, value| key.match(/HEROKU_POSTGRESQL_[A-Z]+_URL/) }
30 | heroku.put_config_vars(app.name, 'DATABASE_URL' => value)
31 | end
32 |
33 | def successful_body(app)
34 | Excon.get("http://#{app.name}.herokuapp.com", :idempotent => true, :expects => 200, :retry_limit => 10).body
35 | end
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License:
2 |
3 | Copyright (C) 2012 Heroku, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/lib/language_pack/rack.rb:
--------------------------------------------------------------------------------
1 | require "language_pack"
2 | require "language_pack/ruby"
3 |
4 | # Rack Language Pack. This is for any non-Rails Rack apps like Sinatra.
5 | class LanguagePack::Rack < LanguagePack::Ruby
6 |
7 | # detects if this is a valid Rack app by seeing if "config.ru" exists
8 | # @return [Boolean] true if it's a Rack app
9 | def self.use?
10 | gemfile_lock? && LanguagePack::Ruby.gem_version('rack')
11 | end
12 |
13 | def name
14 | "Ruby/Rack"
15 | end
16 |
17 | def default_config_vars
18 | super.merge({
19 | "RACK_ENV" => "production"
20 | })
21 | end
22 |
23 | def default_process_types
24 | # let's special case thin here if we detect it
25 | web_process = gem_is_bundled?("thin") ?
26 | "bundle exec thin start -R config.ru -e $RACK_ENV -p $PORT" :
27 | "bundle exec rackup config.ru -p $PORT"
28 |
29 | super.merge({
30 | "web" => web_process
31 | })
32 | end
33 |
34 | private
35 |
36 | # sets up the profile.d script for this buildpack
37 | def setup_profiled
38 | super
39 | set_env_default "RACK_ENV", "production"
40 | end
41 |
42 | end
43 |
44 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: http://rubygems.org/
3 | specs:
4 | activesupport (3.2.13)
5 | i18n (= 0.6.1)
6 | multi_json (~> 1.0)
7 | addressable (2.3.4)
8 | anvil-cli (0.15.0)
9 | progress (~> 2.4.0)
10 | rest-client (~> 1.6.7)
11 | thor (~> 0.15.2)
12 | diff-lcs (1.1.3)
13 | excon (0.16.10)
14 | heroku (2.37.2)
15 | heroku-api (~> 0.3.7)
16 | launchy (>= 0.3.2)
17 | netrc (~> 0.7.7)
18 | rest-client (~> 1.6.1)
19 | rubyzip
20 | heroku-api (0.3.8)
21 | excon (~> 0.16.10)
22 | heroku_hatchet (0.0.1)
23 | activesupport
24 | anvil-cli
25 | excon
26 | heroku-api
27 | thor
28 | i18n (0.6.1)
29 | launchy (2.3.0)
30 | addressable (~> 2.3)
31 | mime-types (1.22)
32 | multi_json (1.7.2)
33 | netrc (0.7.7)
34 | progress (2.4.0)
35 | rake (10.0.4)
36 | rest-client (1.6.7)
37 | mime-types (>= 1.16)
38 | rspec-core (2.13.1)
39 | rspec-expectations (2.12.1)
40 | diff-lcs (~> 1.1.3)
41 | rubyzip (0.9.9)
42 | thor (0.15.4)
43 |
44 | PLATFORMS
45 | ruby
46 |
47 | DEPENDENCIES
48 | excon
49 | heroku
50 | heroku_hatchet
51 | rake
52 | rspec-core
53 | rspec-expectations
54 |
--------------------------------------------------------------------------------
/.idea/heroku-buildpack-ruby.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/spec/rubies_spec.rb:
--------------------------------------------------------------------------------
1 | require_relative 'spec_helper'
2 |
3 | describe "Ruby Versions" do
4 | it "should deploy ruby 1.8.7 properly" do
5 | Hatchet::AnvilApp.new("mri_187", :buildpack => buildpack).deploy do |app, heroku, output|
6 | expect(app).to be_deployed
7 | expect(successful_body(app).chomp).to eq("ruby 1.8.7 (2012-10-12 patchlevel 371) [x86_64-linux]")
8 | end
9 | end
10 |
11 | it "should deploy ruby 1.9.2 properly" do
12 | Hatchet::AnvilApp.new("mri_192", :buildpack => buildpack).deploy do |app, heroku, output|
13 | expect(app).to be_deployed
14 | expect(successful_body(app).chomp).to eq("ruby 1.9.2p320 (2012-04-20 revision 35421) [x86_64-linux]")
15 | end
16 | end
17 |
18 | it "should deploy ruby 1.9.2 properly (git)" do
19 | Hatchet::GitApp.new("mri_192", :buildpack => git_repo).deploy do |app, heroku, output|
20 | expect(app).to be_deployed
21 | expect(successful_body(app).chomp).to eq("ruby 1.9.2p320 (2012-04-20 revision 35421) [x86_64-linux]")
22 | end
23 | end
24 |
25 | it "should deploy ruby 1.9.3 properly" do
26 | Hatchet::AnvilApp.new("mri_193", :buildpack => buildpack).deploy do |app, heroku, output|
27 | expect(app).to be_deployed
28 | expect(successful_body(app).chomp).to eq("ruby 1.9.3p392 (2013-02-22 revision 39386) [x86_64-linux]")
29 | end
30 | end
31 |
32 | it "should deploy ruby 2.0.0 properly" do
33 | Hatchet::AnvilApp.new("mri_200", :buildpack => buildpack).deploy do |app, heroku|
34 | expect(app).to be_deployed
35 | expect(successful_body(app).chomp).to eq("ruby 2.0.0p0 (2013-02-24 revision 39474) [x86_64-linux]")
36 | end
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/lib/language_pack/shell_helpers.rb:
--------------------------------------------------------------------------------
1 | module LanguagePack
2 | module ShellHelpers
3 | # display error message and stop the build process
4 | # @param [String] error message
5 | def error(message)
6 | Kernel.puts " !"
7 | message.split("\n").each do |line|
8 | Kernel.puts " ! #{line.strip}"
9 | end
10 | Kernel.puts " !"
11 | log "exit", :error => message
12 | exit 1
13 | end
14 |
15 | # run a shell comannd and pipe stderr to stdout
16 | # @param [String] command to be run
17 | # @return [String] output of stdout and stderr
18 | def run(command)
19 | %x{ #{command} 2>&1 }
20 | end
21 |
22 | # run a shell command and pipe stderr to /dev/null
23 | # @param [String] command to be run
24 | # @return [String] output of stdout
25 | def run_stdout(command)
26 | %x{ #{command} 2>/dev/null }
27 | end
28 |
29 | # run a shell command and stream the output
30 | # @param [String] command to be run
31 | def pipe(command)
32 | output = ""
33 | IO.popen(command) do |io|
34 | until io.eof?
35 | buffer = io.gets
36 | output << buffer
37 | puts buffer
38 | end
39 | end
40 |
41 | output
42 | end
43 |
44 | # display a topic message
45 | # (denoted by ----->)
46 | # @param [String] topic message to be displayed
47 | def topic(message)
48 | Kernel.puts "-----> #{message}"
49 | $stdout.flush
50 | end
51 |
52 | # display a message in line
53 | # (indented by 6 spaces)
54 | # @param [String] message to be displayed
55 | def puts(message)
56 | message.split("\n").each do |line|
57 | super " #{line.strip}"
58 | end
59 | $stdout.flush
60 | end
61 | end
62 | end
63 |
--------------------------------------------------------------------------------
/vendor/syck_hack.rb:
--------------------------------------------------------------------------------
1 | # :stopdoc:
2 |
3 | # Hack to handle syck's DefaultKey bug
4 | #
5 | # This file is always loaded AFTER either syck or psych are already
6 | # loaded. It then looks at what constants are available and creates
7 | # a consistent view on all rubys.
8 | #
9 | # All this is so that there is always a YAML::Syck::DefaultKey
10 | # class no matter if the full yaml library has loaded or not.
11 | #
12 |
13 | $: << ENV['BUNDLER_LIB_PATH'] if ENV['BUNDLER_LIB_PATH']
14 | require 'bundler/psyched_yaml'
15 |
16 | module YAML
17 | # In newer 1.9.2, there is a Syck toplevel constant instead of it
18 | # being underneith YAML. If so, reference it back under YAML as
19 | # well.
20 | if defined? ::Syck
21 | Syck = ::Syck
22 |
23 | # Otherwise, if there is no YAML::Syck, then we've got just psych
24 | # loaded, so lets define a stub for DefaultKey.
25 | elsif !defined? YAML::Syck
26 | module Syck
27 | class DefaultKey
28 | end
29 | end
30 | end
31 |
32 | # Now that we've got something that is always here, define #to_s
33 | # so when code tries to use this, it at least just shows up like it
34 | # should.
35 | module Syck
36 | class DefaultKey
37 | def to_s
38 | '='
39 | end
40 | end
41 | end
42 | end
43 |
44 | # Sometime in the 1.9 dev cycle, the Syck constant was moved from under YAML
45 | # to be a toplevel constant. So gemspecs created under these versions of Syck
46 | # will have references to Syck::DefaultKey.
47 | #
48 | # So we need to be sure that we reference Syck at the toplevel too so that
49 | # we can always load these kind of gemspecs.
50 | #
51 | if !defined?(Syck)
52 | Syck = YAML::Syck
53 | end
54 |
55 | # Now that we've got Syck setup in all the right places, store
56 | # a reference to the DefaultKey class inside Gem. We do this so that
57 | # if later on YAML, etc are redefined, we've still got a consistent
58 | # place to find the DefaultKey class for comparison.
59 |
60 | module Gem
61 | SyckDefaultKey = YAML::Syck::DefaultKey
62 | end
63 |
64 | # :startdoc:
65 |
--------------------------------------------------------------------------------
/lib/language_pack/rails4.rb:
--------------------------------------------------------------------------------
1 | require "language_pack"
2 | require "language_pack/rails3"
3 |
4 | # Rails 4 Language Pack. This is for all Rails 4.x apps.
5 | class LanguagePack::Rails4 < LanguagePack::Rails3
6 | # detects if this is a Rails 3.x app
7 | # @return [Boolean] true if it's a Rails 3.x app
8 | def self.use?
9 | if gemfile_lock?
10 | rails_version = LanguagePack::Ruby.gem_version('railties')
11 | rails_version >= Gem::Version.new('4.0.0.beta') && rails_version < Gem::Version.new('5.0.0') if rails_version
12 | end
13 | end
14 |
15 | def name
16 | "Ruby/Rails"
17 | end
18 |
19 | def default_process_types
20 | web_process = gem_is_bundled?("thin") ?
21 | "bin/rails server thin -p $PORT -e $RAILS_ENV" :
22 | "bin/rails server -p $PORT -e $RAILS_ENV"
23 | super.merge({
24 | "web" => web_process,
25 | "console" => "bin/rails console"
26 | })
27 | end
28 |
29 | private
30 | def plugins
31 | []
32 | end
33 |
34 | def run_assets_precompile_rake_task
35 | log("assets_precompile") do
36 | setup_database_url_env
37 |
38 | if rake_task_defined?("assets:precompile")
39 | topic("Preparing app for Rails asset pipeline")
40 | if File.exists?("public/assets/manifest.yml")
41 | puts "Detected manifest.yml, assuming assets were compiled locally"
42 | else
43 | ENV["RAILS_GROUPS"] ||= "assets"
44 | ENV["RAILS_ENV"] ||= "production"
45 |
46 | puts "Running: rake assets:precompile"
47 | require 'benchmark'
48 | time = Benchmark.realtime { pipe("env PATH=$PATH:bin bundle exec rake assets:precompile 2>&1") }
49 |
50 | if $?.success?
51 | log "assets_precompile", :status => "success"
52 | puts "Asset precompilation completed (#{"%.2f" % time}s)"
53 | else
54 | log "assets_precompile", :status => "failure"
55 | error "Precompiling assets failed."
56 | end
57 | end
58 | else
59 | puts "Error detecting the assets:precompile task"
60 | end
61 | end
62 | end
63 |
64 | def create_database_yml
65 | end
66 | end
67 |
--------------------------------------------------------------------------------
/support/s3/hmac:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Implement HMAC functionality on top of the OpenSSL digest functions.
3 | # licensed under the terms of the GNU GPL v2
4 | # Copyright 2007 Victor Lowther
5 |
6 | die() {
7 | echo $*
8 | exit 1
9 | }
10 |
11 | check_deps() {
12 | local res=0
13 | while [ $# -ne 0 ]; do
14 | which "${1}" >& /dev/null || { res=1; echo "${1} not found."; }
15 | shift
16 | done
17 | (( res == 0 )) || die "aborting."
18 | }
19 |
20 | # write a byte (passed as hex) to stdout
21 | write_byte() {
22 | # $1 = byte to write
23 | printf "\\x$(printf "%x" ${1})"
24 | }
25 |
26 | # make an hmac pad out of a key.
27 | # this is not the most secure way of doing it, but it is
28 | # the most expedient.
29 | make_hmac_pad() {
30 | # using key in file $1 and byte in $2, create the appropriate hmac pad
31 | # Pad keys out to $3 bytes
32 | # if key is longer than $3, use hash $4 to hash the key first.
33 | local x y a size remainder oifs
34 | (( remainder = ${3} ))
35 | # in case someone else was messing with IFS.
36 | for x in $(echo -n "${1}" | od -v -t u1 | cut -b 9-);
37 | do
38 | write_byte $((${x} ^ ${2}))
39 | (( remainder -= 1 ))
40 | done
41 | for ((y=0; remainder - y ;y++)); do
42 | write_byte $((0 ^ ${2}))
43 | done
44 | }
45 |
46 | # utility functions for making hmac pads
47 | hmac_ipad() {
48 | make_hmac_pad "${1}" 0x36 ${2} "${3}"
49 | }
50 |
51 | hmac_opad() {
52 | make_hmac_pad "${1}" 0x5c ${2} "${3}"
53 | }
54 |
55 | # hmac something
56 | do_hmac() {
57 | # $1 = algo to use. Must be one that openssl knows about
58 | # $2 = keyfile to use
59 | # $3 = file to hash. uses stdin if none is given.
60 | # accepts input on stdin, leaves it on stdout.
61 | # Output is binary, if you want something else pipe it accordingly.
62 | local blocklen keysize x
63 | case "${1}" in
64 | sha) blocklen=64 ;;
65 | sha1) blocklen=64 ;;
66 | md5) blocklen=64 ;;
67 | md4) blocklen=64 ;;
68 | sha256) blocklen=64 ;;
69 | sha512) blocklen=128 ;;
70 | *) die "Unknown hash ${1} passed to hmac!" ;;
71 | esac
72 | cat <(hmac_ipad ${2} ${blocklen} "${1}") "${3:--}" | openssl dgst "-${1}" -binary | \
73 | cat <(hmac_opad ${2} ${blocklen} "${1}") - | openssl dgst "-${1}" -binary
74 | }
75 |
76 | [[ ${1} ]] || die "Must pass the name of the hash function to use to ${0}".
77 |
78 | check_deps od openssl
79 | do_hmac "${@}"
80 |
--------------------------------------------------------------------------------
/lib/language_pack/rails2.rb:
--------------------------------------------------------------------------------
1 | require "fileutils"
2 | require "language_pack"
3 | require "language_pack/rack"
4 |
5 | # Rails 2 Language Pack. This is for any Rails 2.x apps.
6 | class LanguagePack::Rails2 < LanguagePack::Ruby
7 |
8 | # detects if this is a valid Rails 2 app
9 | # @return [Boolean] true if it's a Rails 2 app
10 | def self.use?
11 | if gemfile_lock?
12 | rails_version = LanguagePack::Ruby.gem_version('rails')
13 | rails_version >= Gem::Version.new('2.0.0') && rails_version < Gem::Version.new('3.0.0') if rails_version
14 | end
15 | end
16 |
17 | def name
18 | "Ruby/Rails"
19 | end
20 |
21 | def default_config_vars
22 | super.merge({
23 | "RAILS_ENV" => "production",
24 | "RACK_ENV" => "production"
25 | })
26 | end
27 |
28 | def default_process_types
29 | web_process = gem_is_bundled?("thin") ?
30 | "bundle exec thin start -e $RAILS_ENV -p $PORT" :
31 | "bundle exec ruby script/server -p $PORT"
32 |
33 | super.merge({
34 | "web" => web_process,
35 | "worker" => "bundle exec rake jobs:work",
36 | "console" => "bundle exec script/console"
37 | })
38 | end
39 |
40 | def compile
41 | super
42 | install_plugins
43 | end
44 |
45 | private
46 |
47 | # list of plugins to be installed
48 | # @return [Array] resulting list in a String Array
49 | def plugins
50 | %w( rails_log_stdout )
51 | end
52 |
53 | # the root path of where the plugins are to be installed from
54 | # @return [String] the resulting path
55 | def plugin_root
56 | File.expand_path("../../../vendor/plugins", __FILE__)
57 | end
58 |
59 | # vendors all the plugins into the slug
60 | def install_plugins
61 | topic "Rails plugin injection"
62 | plugins.each { |plugin| install_plugin(plugin) }
63 | end
64 |
65 | # vendors an individual plugin
66 | # @param [String] name of the plugin
67 | def install_plugin(name)
68 | plugin_dir = "vendor/plugins/#{name}"
69 | return if File.exist?(plugin_dir)
70 | puts "Injecting #{name}"
71 | FileUtils.mkdir_p plugin_dir
72 | Dir.chdir(plugin_dir) do |dir|
73 | run("curl #{VENDOR_URL}/#{name}.tgz -s -o - | tar xzf -")
74 | end
75 | end
76 |
77 | # most rails apps need a database
78 | # @return [Array] shared database addon
79 | def add_dev_database_addon
80 | ['heroku-postgresql:dev']
81 | end
82 |
83 | # sets up the profile.d script for this buildpack
84 | def setup_profiled
85 | super
86 | set_env_default "RACK_ENV", "production"
87 | set_env_default "RAILS_ENV", "production"
88 | end
89 |
90 | end
91 |
92 |
--------------------------------------------------------------------------------
/.idea/.rakeTasks:
--------------------------------------------------------------------------------
1 |
2 |
8 |
--------------------------------------------------------------------------------
/lib/language_pack/rails3.rb:
--------------------------------------------------------------------------------
1 | require "language_pack"
2 | require "language_pack/rails2"
3 |
4 | # Rails 3 Language Pack. This is for all Rails 3.x apps.
5 | class LanguagePack::Rails3 < LanguagePack::Rails2
6 | # detects if this is a Rails 3.x app
7 | # @return [Boolean] true if it's a Rails 3.x app
8 | def self.use?
9 | if gemfile_lock?
10 | rails_version = LanguagePack::Ruby.gem_version('railties')
11 | rails_version >= Gem::Version.new('3.0.0') && rails_version < Gem::Version.new('4.0.0') if rails_version
12 | end
13 | end
14 |
15 | def name
16 | "Ruby/Rails"
17 | end
18 |
19 | def default_process_types
20 | # let's special case thin here
21 | web_process = gem_is_bundled?("thin") ?
22 | "bundle exec thin start -R config.ru -e $RAILS_ENV -p $PORT" :
23 | "bundle exec rails server -p $PORT"
24 |
25 | super.merge({
26 | "web" => web_process,
27 | "console" => "bundle exec rails console"
28 | })
29 | end
30 |
31 | private
32 |
33 | def plugins
34 | super.concat(%w( rails3_serve_static_assets )).uniq
35 | end
36 |
37 | # runs the tasks for the Rails 3.1 asset pipeline
38 | def run_assets_precompile_rake_task
39 | log("assets_precompile") do
40 | setup_database_url_env
41 |
42 | if rake_task_defined?("assets:precompile")
43 | topic("Preparing app for Rails asset pipeline")
44 | if File.exists?("public/assets/manifest.yml")
45 | puts "Detected manifest.yml, assuming assets were compiled locally"
46 | else
47 | ENV["RAILS_GROUPS"] ||= "assets"
48 | ENV["RAILS_ENV"] ||= "production"
49 |
50 | puts "Running: rake assets:precompile"
51 | require 'benchmark'
52 | time = Benchmark.realtime { pipe("env PATH=$PATH:bin bundle exec rake assets:precompile 2>&1") }
53 |
54 | if $?.success?
55 | log "assets_precompile", :status => "success"
56 | puts "Asset precompilation completed (#{"%.2f" % time}s)"
57 | else
58 | log "assets_precompile", :status => "failure"
59 | puts "Precompiling assets failed, enabling runtime asset compilation"
60 | install_plugin("rails31_enable_runtime_asset_compilation")
61 | puts "Please see this article for troubleshooting help:"
62 | puts "http://devcenter.heroku.com/articles/rails31_heroku_cedar#troubleshooting"
63 | end
64 | end
65 | end
66 | end
67 | end
68 |
69 | # setup the database url as an environment variable
70 | def setup_database_url_env
71 | ENV["DATABASE_URL"] ||= begin
72 | # need to use a dummy DATABASE_URL here, so rails can load the environment
73 | scheme =
74 | if gem_is_bundled?("pg")
75 | "postgres"
76 | elsif gem_is_bundled?("mysql")
77 | "mysql"
78 | elsif gem_is_bundled?("mysql2")
79 | "mysql2"
80 | elsif gem_is_bundled?("sqlite3") || gem_is_bundled?("sqlite3-ruby")
81 | "sqlite3"
82 | end
83 | "#{scheme}://user:pass@127.0.0.1/dbname"
84 | end
85 | end
86 | end
87 |
--------------------------------------------------------------------------------
/lib/language_pack/base.rb:
--------------------------------------------------------------------------------
1 | require "language_pack"
2 | require "pathname"
3 | require "yaml"
4 | require "digest/sha1"
5 | require "language_pack/shell_helpers"
6 |
7 | Encoding.default_external = Encoding::UTF_8 if defined?(Encoding)
8 |
9 | # abstract class that all the Ruby based Language Packs inherit from
10 | class LanguagePack::Base
11 | include LanguagePack::ShellHelpers
12 |
13 | VENDOR_URL = "https://s3.amazonaws.com/heroku-buildpack-ruby"
14 |
15 | attr_reader :build_path, :cache_path
16 |
17 | # changes directory to the build_path
18 | # @param [String] the path of the build dir
19 | # @param [String] the path of the cache dir
20 | def initialize(build_path, cache_path=nil)
21 | @build_path = build_path
22 | @cache_path = cache_path
23 | @id = Digest::SHA1.hexdigest("#{Time.now.to_f}-#{rand(1000000)}")[0..10]
24 |
25 | Dir.chdir build_path
26 | end
27 |
28 | def self.===(build_path)
29 | raise "must subclass"
30 | end
31 |
32 | # name of the Language Pack
33 | # @return [String] the result
34 | def name
35 | raise "must subclass"
36 | end
37 |
38 | # list of default addons to install
39 | def default_addons
40 | raise "must subclass"
41 | end
42 |
43 | # config vars to be set on first push.
44 | # @return [Hash] the result
45 | # @not: this is only set the first time an app is pushed to.
46 | def default_config_vars
47 | raise "must subclass"
48 | end
49 |
50 | # process types to provide for the app
51 | # Ex. for rails we provide a web process
52 | # @return [Hash] the result
53 | def default_process_types
54 | raise "must subclass"
55 | end
56 |
57 | # this is called to build the slug
58 | def compile
59 | end
60 |
61 | # collection of values passed for a release
62 | # @return [String] in YAML format of the result
63 | def release
64 | setup_language_pack_environment
65 |
66 | {
67 | "addons" => default_addons,
68 | "default_process_types" => default_process_types
69 | }.to_yaml
70 | end
71 |
72 | # log output
73 | # Ex. log "some_message", "here", :someattr="value"
74 | def log(*args)
75 | args.concat [:id => @id]
76 | args.concat [:framework => self.class.to_s.split("::").last.downcase]
77 |
78 | start = Time.now.to_f
79 | log_internal args, :start => start
80 |
81 | if block_given?
82 | begin
83 | ret = yield
84 | finish = Time.now.to_f
85 | log_internal args, :status => "complete", :finish => finish, :elapsed => (finish - start)
86 | return ret
87 | rescue StandardError => ex
88 | finish = Time.now.to_f
89 | log_internal args, :status => "error", :finish => finish, :elapsed => (finish - start), :message => ex.message
90 | raise ex
91 | end
92 | end
93 | end
94 |
95 | private ##################################
96 |
97 | # sets up the environment variables for the build process
98 | def setup_language_pack_environment
99 | end
100 |
101 | def add_to_profiled(string)
102 | FileUtils.mkdir_p "#{build_path}/.profile.d"
103 | File.open("#{build_path}/.profile.d/ruby.sh", "a") do |file|
104 | file.puts string
105 | end
106 | end
107 |
108 | def set_env_default(key, val)
109 | add_to_profiled "export #{key}=${#{key}:-#{val}}"
110 | end
111 |
112 | def set_env_override(key, val)
113 | add_to_profiled %{export #{key}="#{val.gsub('"','\"')}"}
114 | end
115 |
116 | def log_internal(*args)
117 | message = build_log_message(args)
118 | %x{ logger -p user.notice -t "slugc[$$]" "buildpack-ruby #{message}" }
119 | end
120 |
121 | def build_log_message(args)
122 | args.map do |arg|
123 | case arg
124 | when Float then "%0.2f" % arg
125 | when Array then build_log_message(arg)
126 | when Hash then arg.map { |k,v| "#{k}=#{build_log_message([v])}" }.join(" ")
127 | else arg
128 | end
129 | end.join(" ")
130 | end
131 |
132 | # create a Pathname of the cache dir
133 | # @return [Pathname] the cache dir
134 | def cache_base
135 | Pathname.new(cache_path)
136 | end
137 |
138 | # removes the the specified
139 | # @param [String] relative path from the cache_base
140 | def cache_clear(path)
141 | target = (cache_base + path)
142 | target.exist? && target.rmtree
143 | end
144 |
145 | # write cache contents
146 | # @param [String] path of contents to store. it will be stored using this a relative path from the cache_base.
147 | # @param [Boolean] defaults to true. if set to true, the cache store directory will be cleared before writing to it.
148 | def cache_store(path, clear_first=true)
149 | cache_clear(path) if clear_first
150 | cache_copy path, (cache_base + path)
151 | end
152 |
153 | # load cache contents
154 | # @param [String] relative path of the cache contents
155 | def cache_load(path)
156 | cache_copy (cache_base + path), path
157 | end
158 |
159 | # copy cache contents
160 | # @param [String] source directory
161 | # @param [String] destination directory
162 | def cache_copy(from, to)
163 | return false unless File.exist?(from)
164 | FileUtils.mkdir_p File.dirname(to)
165 | system("cp -a #{from}/. #{to}")
166 | end
167 |
168 | # check if the cache content exists
169 | # @param [String] relative path of the cache contents
170 | # @param [Boolean] true if the path exists in the cache and false if otherwise
171 | def cache_exists?(path)
172 | File.exists?(cache_base + path)
173 | end
174 | end
175 |
176 |
--------------------------------------------------------------------------------
/support/s3/s3:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # basic amazon s3 operations
3 | # Licensed under the terms of the GNU GPL v2
4 | # Copyright 2007 Victor Lowther
5 |
6 | set -e
7 |
8 | basedir="$( cd -P "$( dirname "$0" )" && pwd )"
9 | PATH="$basedir:$PATH"
10 |
11 | # print a message and bail
12 | die() {
13 | echo $*
14 | exit 1
15 | }
16 |
17 | # check to see if the variable name passed exists and holds a value.
18 | # Die if it does not.
19 | check_or_die() {
20 | [[ ${!1} ]] || die "Environment variable ${1} is not set."
21 | }
22 |
23 | # check to see if we have all the needed S3 variables defined.
24 | # Bail if we do not.
25 | check_s3() {
26 | local sak x
27 | for x in S3_ACCESS_KEY_ID S3_SECRET_ACCESS_KEY; do
28 | check_or_die ${x};
29 | done
30 | sak="$(echo -n $S3_SECRET_ACCESS_KEY | wc -c)"
31 | (( ${sak%%[!0-9 ]*} == 40 )) || \
32 | die "S3 Secret Access Key is not exactly 40 bytes long. Please fix it."
33 | }
34 | # check to see if our external dependencies exist
35 | check_dep() {
36 | local res=0
37 | while [[ $# -ne 0 ]]; do
38 | which "${1}" >& /dev/null || { res=1; echo "${1} not found."; }
39 | shift
40 | done
41 | (( res == 0 )) || die "aborting."
42 | }
43 |
44 | check_deps() {
45 | check_dep openssl date hmac cat grep curl
46 | check_s3
47 | }
48 |
49 | urlenc() {
50 | # $1 = string to url encode
51 | # output is on stdout
52 | # we don't urlencode everything, just enough stuff.
53 | echo -n "${1}" |
54 | sed 's/%/%25/g
55 | s/ /%20/g
56 | s/#/%23/g
57 | s/\$/%24/g
58 | s/\&/%26/g
59 | s/+/%2b/g
60 | s/,/%2c/g
61 | s/:/%3a/g
62 | s/;/%3b/g
63 | s/?/%3f/g
64 | s/@/%40/g
65 | s/ /%09/g'
66 | }
67 |
68 | xmldec() {
69 | # no parameters.
70 | # accept input on stdin, put it on stdout.
71 | # patches accepted to get more stuff
72 | sed 's/\"/\"/g
73 | s/\&/\&/g
74 | s/\<//g'
76 | }
77 |
78 | ## basic S3 functionality. x-amz-header functionality is not implemented.
79 | # make an S3 signature string, which will be output on stdout.
80 | s3_signature_string() {
81 | # $1 = HTTP verb
82 | # $2 = date string, must be in UTC
83 | # $3 = bucket name, if any
84 | # $4 = resource path, if any
85 | # $5 = content md5, if any
86 | # $6 = content MIME type, if any
87 | # $7 = canonicalized headers, if any
88 | # signature string will be output on stdout
89 | local verr="Must pass a verb to s3_signature_string!"
90 | local verb="${1:?verr}"
91 | local bucket="${3}"
92 | local resource="${4}"
93 | local derr="Must pass a date to s3_signature_string!"
94 | local date="${2:?derr}"
95 | local mime="${6}"
96 | local md5="${5}"
97 | local headers="${7}"
98 | printf "%s\n%s\n%s\n%s\n%s\n%s%s" \
99 | "${verb}" "${md5}" "${mime}" "${date}" \
100 | "${headers}" "${bucket}" "${resource}" | \
101 | hmac sha1 "${S3_SECRET_ACCESS_KEY}" | openssl base64 -e -a
102 | }
103 |
104 | # cheesy, but it is the best way to have multiple headers.
105 | curl_headers() {
106 | # each arg passed will be output on its own line
107 | local parms=$#
108 | for ((;$#;)); do
109 | echo "header = \"${1}\""
110 | shift
111 | done
112 | }
113 |
114 | s3_curl() {
115 | # invoke curl to do all the heavy HTTP lifting
116 | # $1 = method (one of GET, PUT, or DELETE. HEAD is not handled yet.)
117 | # $2 = remote bucket.
118 | # $3 = remote name
119 | # $4 = local name.
120 | local bucket remote date sig md5 arg inout headers
121 | # header handling is kinda fugly, but it works.
122 | bucket="${2:+/${2}}/" # slashify the bucket
123 | remote="$(urlenc "${3}")" # if you don't, strange things may happen.
124 | stdopts="--connect-timeout 10 --fail --silent"
125 | [[ $CURL_S3_DEBUG == true ]] && stdopts="${stdopts} --show-error --fail"
126 | case "${1}" in
127 | GET) arg="-o" inout="${4:--}" # stdout if no $4
128 | ;;
129 | PUT) [[ ${2} ]] || die "PUT can has bucket?"
130 | if [[ ! ${3} ]]; then
131 | arg="-X PUT"
132 | headers[${#headers[@]}]="Content-Length: 0"
133 | elif [[ -f ${4} ]]; then
134 | md5="$(openssl dgst -md5 -binary "${4}"|openssl base64 -e -a)"
135 | arg="-T" inout="${4}"
136 | headers[${#headers[@]}]="x-amz-acl: public-read"
137 | headers[${#headers[@]}]="Expect: 100-continue"
138 | else
139 | die "Cannot write non-existing file ${4}"
140 | fi
141 | ;;
142 | DELETE) arg="-X DELETE"
143 | ;;
144 | HEAD) arg="-I" ;;
145 | *) die "Unknown verb ${1}. It probably would not have worked anyways." ;;
146 | esac
147 | date="$(TZ=UTC date '+%a, %e %b %Y %H:%M:%S %z')"
148 | sig=$(s3_signature_string ${1} "${date}" "${bucket}" "${remote}" "${md5}" "" "x-amz-acl:public-read")
149 |
150 | headers[${#headers[@]}]="Authorization: AWS ${S3_ACCESS_KEY_ID}:${sig}"
151 | headers[${#headers[@]}]="Date: ${date}"
152 | [[ ${md5} ]] && headers[${#headers[@]}]="Content-MD5: ${md5}"
153 | curl ${arg} "${inout}" ${stdopts} -o - -K <(curl_headers "${headers[@]}") \
154 | "http://s3.amazonaws.com${bucket}${remote}"
155 | return $?
156 | }
157 |
158 | s3_put() {
159 | # $1 = remote bucket to put it into
160 | # $2 = remote name to put
161 | # $3 = file to put. This must be present if $2 is.
162 | s3_curl PUT "${1}" "${2}" "${3:-${2}}"
163 | return $?
164 | }
165 |
166 | s3_get() {
167 | # $1 = bucket to get file from
168 | # $2 = remote file to get
169 | # $3 = local file to get into. Will be overwritten if it exists.
170 | # If this contains a path, that path must exist before calling this.
171 | s3_curl GET "${1}" "${2}" "${3:-${2}}"
172 | return $?
173 | }
174 |
175 | s3_test() {
176 | # same args as s3_get, but uses the HEAD verb instead of the GET verb.
177 | s3_curl HEAD "${1}" "${2}" >/dev/null
178 | return $?
179 | }
180 |
181 | # Hideously ugly, but it works well enough.
182 | s3_buckets() {
183 | s3_get |grep -o '[^>]*' |sed 's/<[^>]*>//g' |xmldec
184 | return $?
185 | }
186 |
187 | # this will only return the first thousand entries, alas
188 | # Mabye some kind soul can fix this without writing an XML parser in bash?
189 | # Also need to add xml entity handling.
190 | s3_list() {
191 | # $1 = bucket to list
192 | [ "x${1}" == "x" ] && return 1
193 | s3_get "${1}" |grep -o '[^>]*' |sed 's/<[^>]*>//g'| xmldec
194 | return $?
195 | }
196 |
197 | s3_delete() {
198 | # $1 = bucket to delete from
199 | # $2 = item to delete
200 | s3_curl DELETE "${1}" "${2}"
201 | return $?
202 | }
203 |
204 | # because this uses s3_list, it suffers from the same flaws.
205 | s3_rmrf() {
206 | # $1 = bucket to delete everything from
207 | s3_list "${1}" | while read f; do
208 | s3_delete "${1}" "${f}";
209 | done
210 | }
211 |
212 | check_deps
213 | case $1 in
214 | put) shift; s3_put "$@" ;;
215 | get) shift; s3_get "$@" ;;
216 | rm) shift; s3_delete "$@" ;;
217 | ls) shift; s3_list "$@" ;;
218 | test) shift; s3_test "$@" ;;
219 | buckets) s3_buckets ;;
220 | rmrf) shift; s3_rmrf "$@" ;;
221 | *) die "Unknown command ${1}."
222 | ;;
223 | esac
224 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Heroku buildpack: Ruby
2 | ======================
3 |
4 | This is a [Heroku buildpack](http://devcenter.heroku.com/articles/buildpacks) for Ruby, Rack, and Rails apps. It uses [Bundler](http://gembundler.com) for dependency management.
5 |
6 | Usage
7 | -----
8 |
9 | ### Ruby
10 |
11 | Example Usage:
12 |
13 | $ ls
14 | Gemfile Gemfile.lock
15 |
16 | $ heroku create --stack cedar --buildpack https://github.com/heroku/heroku-buildpack-ruby.git
17 |
18 | $ git push heroku master
19 | ...
20 | -----> Heroku receiving push
21 | -----> Fetching custom buildpack
22 | -----> Ruby app detected
23 | -----> Installing dependencies using Bundler version 1.1.rc
24 | Running: bundle install --without development:test --path vendor/bundle --deployment
25 | Fetching gem metadata from http://rubygems.org/..
26 | Installing rack (1.3.5)
27 | Using bundler (1.1.rc)
28 | Your bundle is complete! It was installed into ./vendor/bundle
29 | Cleaning up the bundler cache.
30 | -----> Discovering process types
31 | Procfile declares types -> (none)
32 | Default types for Ruby -> console, rake
33 |
34 | The buildpack will detect your app as Ruby if it has a `Gemfile` and `Gemfile.lock` files in the root directory. It will then proceed to run `bundle install` after setting up the appropriate environment for [ruby](http://ruby-lang.org) and [Bundler](http://gembundler.com).
35 |
36 | #### Run the Tests
37 |
38 | Clone the repo, then `bundle install` then clone the test fixtures by running:
39 |
40 | ```sh
41 | $ hatchet install
42 | ```
43 |
44 | Now run the tests:
45 |
46 | ```sh
47 | $ bundle exec rspec spec
48 | ```
49 |
50 | Now go take a nap or something for a really long time.
51 |
52 | #### Bundler
53 |
54 | For non-windows `Gemfile.lock` files, the `--deployment` flag will be used. In the case of windows, the Gemfile.lock will be deleted and Bundler will do a full resolve so native gems are handled properly. The `vendor/bundle` directory is cached between builds to allow for faster `bundle install` times. `bundle clean` is used to ensure no stale gems are stored between builds.
55 |
56 | ### Rails 2
57 |
58 | Example Usage:
59 |
60 | $ ls
61 | app config db doc Gemfile Gemfile.lock lib log public Rakefile README script test tmp vendor
62 |
63 | $ ls config/environment.rb
64 | config/environment.rb
65 |
66 | $ heroku create --stack cedar --buildpack https://github.com/heroku/heroku-buildpack-ruby.git
67 |
68 | $ git push heroku master
69 | ...
70 | -----> Heroku receiving push
71 | -----> Ruby/Rails app detected
72 | -----> Installing dependencies using Bundler version 1.1.rc
73 | ...
74 | -----> Writing config/database.yml to read from DATABASE_URL
75 | -----> Rails plugin injection
76 | Injecting rails_log_stdout
77 | -----> Discovering process types
78 | Procfile declares types -> (none)
79 | Default types for Ruby/Rails -> console, rake, web, worker
80 |
81 | The buildpack will detect your app as a Rails 2 app if it has a `environment.rb` file in the `config` directory.
82 |
83 | #### Rails Log STDOUT
84 | A [rails_log_stdout](http://github.com/ddollar/rails_log_stdout) is installed by default so Rails' logger will log to STDOUT and picked up by Heroku's [logplex](http://github.com/heroku/logplex).
85 |
86 | #### Auto Injecting Plugins
87 |
88 | Any vendored plugin can be stopped from being installed by creating the directory it's installed to in the slug. For instance, to prevent rails_log_stdout plugin from being injected, add `vendor/plugins/rails_log_stdout/.gitkeep` to your git repo.
89 |
90 | ### Rails 3
91 |
92 | Example Usage:
93 |
94 | $ ls
95 | app config config.ru db doc Gemfile Gemfile.lock lib log Procfile public Rakefile README script tmp vendor
96 |
97 | $ ls config/application.rb
98 | config/application.rb
99 |
100 | $ heroku create --stack cedar --buildpack https://github.com/heroku/heroku-buildpack-ruby.git
101 |
102 | $ git push heroku master
103 | -----> Heroku receiving push
104 | -----> Ruby/Rails app detected
105 | -----> Installing dependencies using Bundler version 1.1.rc
106 | Running: bundle install --without development:test --path vendor/bundle --deployment
107 | ...
108 | -----> Writing config/database.yml to read from DATABASE_URL
109 | -----> Preparing app for Rails asset pipeline
110 | Running: rake assets:precompile
111 | -----> Rails plugin injection
112 | Injecting rails_log_stdout
113 | Injecting rails3_serve_static_assets
114 | -----> Discovering process types
115 | Procfile declares types -> web
116 | Default types for Ruby/Rails -> console, rake, worker
117 |
118 | The buildpack will detect your apps as a Rails 3 app if it has an `application.rb` file in the `config` directory.
119 |
120 | #### Assets
121 |
122 | To enable static assets being served on the dyno, [rails3_serve_static_assets](http://github.com/pedro/rails3_serve_static_assets) is installed by default. If the [execjs gem](http://github.com/sstephenson/execjs) is detected then [node.js](http://github.com/joyent/node) will be vendored. The `assets:precompile` rake task will get run if no `public/manifest.yml` is detected. See [this article](http://devcenter.heroku.com/articles/rails31_heroku_cedar) on how rails 3.1 works on cedar.
123 |
124 | Hacking
125 | -------
126 |
127 | To use this buildpack, fork it on Github. Push up changes to your fork, then create a test app with `--buildpack ` and push to it.
128 |
129 | To change the vendored binaries for Bundler, [Node.js](http://github.com/joyent/node), and rails plugins, use the rake tasks provided by the `Rakefile`. You'll need an S3-enabled AWS account and a bucket to store your binaries in as well as the [vulcan](http://github.com/heroku/vulcan) gem to build the binaries on heroku.
130 |
131 | For example, you can change the vendored version of Bundler to 1.1.rc.
132 |
133 | First you'll need to build a Heroku-compatible version of Node.js:
134 |
135 | $ export AWS_ID=xxx AWS_SECRET=yyy S3_BUCKET=zzz
136 | $ s3 create $S3_BUCKET
137 | $ rake gem:install[bundler,1.1.rc]
138 |
139 | Open `lib/language_pack/ruby.rb` in your editor, and change the following line:
140 |
141 | BUNDLER_VERSION = "1.1.rc"
142 |
143 | Open `lib/language_pack/base.rb` in your editor, and change the following line:
144 |
145 | VENDOR_URL = "https://s3.amazonaws.com/zzz"
146 |
147 | Commit and push the changes to your buildpack to your Github fork, then push your sample app to Heroku to test. You should see:
148 |
149 | -----> Installing dependencies using Bundler version 1.1.rc
150 |
151 | NOTE: You'll need to vendor the plugins, node, Bundler, and libyaml by running the rake tasks for the buildpack to work properly.
152 |
153 | Flow
154 | ----
155 |
156 | Here's the basic flow of how the buildpack works:
157 |
158 | Ruby (Gemfile and Gemfile.lock is detected)
159 |
160 | * runs Bundler
161 | * installs binaries
162 | * installs node if the gem execjs is detected
163 | * runs `rake assets:precompile` if the rake task is detected
164 |
165 | Rack (config.ru is detected)
166 |
167 | * everything from Ruby
168 | * sets RACK_ENV=production
169 |
170 | Rails 2 (config/environment.rb is detected)
171 |
172 | * everything from Rack
173 | * sets RAILS_ENV=production
174 | * install rails 2 plugins
175 | * [rails_log_stdout](http://github.com/ddollar/rails_log_stdout)
176 |
177 | Rails 3 (config/application.rb is detected)
178 |
179 | * everything from Rails 2
180 | * install rails 3 plugins
181 | * [rails3_server_static_assets](https://github.com/pedro/rails3_serve_static_assets)
182 |
183 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## v61 (4/18/2013)
2 |
3 | Features:
4 |
5 | * Start caching the rubygems version used.
6 |
7 | Bugfixes:
8 |
9 | * Rebuild bundler cache if rubygems 2 is detected. Bugfixes in later rubygems.
10 |
11 | ## v60 (4/17/2013)
12 |
13 | Security:
14 |
15 | * Disable Java RMI Remote Classloading for CVE-2013-1537,
16 |
17 | ## v59 (4/4/2013)
18 |
19 | Bugfixes:
20 |
21 | * Change JVM S3 bucket
22 |
23 | ## v58 (3/19/2013)
24 |
25 | Bugfixes:
26 |
27 | * Fix ruby 1.8.7 not being able to compile native extensions
28 |
29 | ## v57 (3/18/2013)
30 |
31 | Bugfixes:
32 |
33 | * Fix git gemspec bug in bundler
34 |
35 | ## v56 (3/11/2013)
36 |
37 | Bugfixes:
38 |
39 | * Upgrade bundler to 1.3.2 to fix --dry-clean/Would have removed bug in bundle clean, part 2.
40 |
41 | ## v55 (3/7/2013)
42 |
43 | Bugfixes:
44 |
45 | * Revert back to Bundler 1.3.0.pre.5, see https://gist.github.com/mattonrails/e063caf86962995e7ba0
46 |
47 | ## v54 (3/7/2013)
48 |
49 | Bugfixes:
50 |
51 | * Upgrade bundler to 1.3.2 to fix --dry-clean/Would have removed bug in bundle clean
52 |
53 | ## v53 (3/6/2013)
54 |
55 | Bugfixes:
56 |
57 | * bin/detect for Rails 3 and 4 will use railties for detection vs the rails gem
58 | * bin/detect does not error out when Gemfile + Gemfile.lock are missing
59 |
60 | ## v52 (2/25/2013)
61 |
62 | Bugfixes:
63 |
64 | * Revert back to 1.3.0.pre.5 due to bundler warnings
65 |
66 | ## v51 (2/25/2013)
67 |
68 | Features:
69 |
70 | * Initial Rails 4 beta support
71 | * Upgrade bundler to 1.3.0
72 |
73 | Bugfixes:
74 |
75 | * Better buildpack detection through Gemfile.lock gems
76 |
77 | ## v50 (1/31/2013)
78 |
79 | Features:
80 |
81 | * Restore ruby deploys back to normal
82 |
83 | ## v49 (1/30/2013)
84 |
85 | Features:
86 |
87 | * Re-enable ruby deploys for apps just using the heroku cache
88 | * Display ruby version change when busting the cache
89 |
90 | ## v48 (1/30/2013)
91 |
92 | Features:
93 |
94 | * Update deploy error message copy to link to status incident.
95 |
96 | ## v47 (1/30/2013)
97 |
98 | Features:
99 |
100 | * Disable ruby deploys due to rubygems.org compromise
101 |
102 | ## v46 (1/10/2013)
103 |
104 | Features:
105 |
106 | * Upgrade Bundler to 1.3.0.pre.5
107 | * bundler binstubs now go in vendor/bundle/bin
108 |
109 | ## v45 (12/14/2012)
110 |
111 | Features:
112 |
113 | * Stop setting env vars in bin/release now that login-shell is released
114 | * Enable Invoke Dynamic on JRuby by default
115 | * GEM_PATH is now updated on each push
116 |
117 | ## v44 (12/14/2012)
118 |
119 | Faulty Release
120 |
121 | ## v43 (12/13/2012)
122 |
123 | Features:
124 |
125 | * Upgrade Bundler to 1.3.0.pre.2
126 |
127 | ## v42 (11/26/2012)
128 |
129 | Features:
130 |
131 | * Upgrade Bundler to 1.2.2 to fix Ruby 2.0.0/YAML issues
132 |
133 | ## v41 (11/1/2012)
134 |
135 | Features:
136 |
137 | * Enable ruby 2.0.0 support for testing
138 |
139 | ## v40 (10/14/2012)
140 |
141 | Features:
142 |
143 | * Cache version of the buildpack we used to deploy
144 | * Purge cache when v38 is detected
145 |
146 | ## v39 (10/14/2012)
147 |
148 | Bugfixes:
149 |
150 | * Don't display cache clearing message for new apps
151 | * Actually clear bundler cache on ruby version change
152 |
153 | ## v38 (10/14/2012)
154 |
155 | Bugfixes:
156 |
157 | * Stop bundle cache from continually growing
158 |
159 | ## v37 (10/12/2012)
160 |
161 | Bugfixes:
162 |
163 | * Remove temporary workaround from v36.
164 | * Clear bundler cache upon Ruby version change
165 |
166 | ## v36 (10/12/2012)
167 |
168 | Bugfixes:
169 |
170 | * Always clear the cache for ruby 1.9.3 as a temporary workaround due to the security upgrade
171 |
172 | ## v35 (9/19/2012)
173 |
174 | Features:
175 |
176 | * Upgrade to Bundler 1.2.1
177 | * Display bundle clean output
178 | * More resilent to rubygems.org API outages
179 |
180 | Bugfixes:
181 |
182 | * `bundle clean` works again
183 |
184 | ## v34 (8/30/2012)
185 |
186 | Features:
187 |
188 | * Upgrade to Bundler 1.2.0
189 |
190 | ## v33 (8/9/2012)
191 |
192 | Features:
193 |
194 | * Upgrade to Bundler 1.2.0.rc.2
195 | * vendor JDK7 for JRuby, but disable invoke dynamic
196 |
197 | ## v29 (7/19/2012)
198 |
199 | Features:
200 |
201 | * support .profile.d/ruby.sh
202 | * sync stdout so that the buildpack streams even in non-interactive shells
203 | * Upgrade to Bundler 1.2.0.rc
204 |
205 | ## v28 (7/16/2012)
206 |
207 | Features:
208 |
209 | * Vendor OpenJDK6 into slug when using JRuby
210 | * ruby version support for ruby 1.8.7 via bundler's ruby DSL
211 |
212 | Bugfixes:
213 |
214 | * sqlite3 error gets displayed again
215 |
216 | ## v27 (6/14/2012)
217 |
218 | Bugfixes:
219 |
220 | * Remove `vendor/bundle` message only appears when dir actually exists
221 |
222 | ## v26 (6/14/2012)
223 |
224 | Features:
225 |
226 | * print message when assets:precompile finishes successfully
227 | * Remove `vendor/bundle` if user commits it to their git repo.
228 |
229 | ## v25 (6/12/2012)
230 |
231 | Features:
232 |
233 | * support "ruby-xxx-jruby-yyy" for jruby detection packages
234 |
235 | ## v24 (6/7/2012)
236 |
237 | Features:
238 |
239 | * removes bundler cache in the slug, to minimize slug size (@stevenh512, #16)
240 | * optimize push time with caching
241 |
242 | ## v23 (5/8/2012)
243 |
244 | Bugfixes:
245 |
246 | * fix ruby version bug with "fatal:-Not-a-git-repository"
247 |
248 | ## v22 (5/7/2012)
249 |
250 | Features:
251 |
252 | * bundler 1.2.0.pre
253 | * ruby version support for ruby 1.9.2/1.9.3 via bundler's ruby DSL
254 |
255 | Deprecation:
256 |
257 | * ENV['RUBY_VERSION'] in favor of bundler's ruby DSL
258 |
259 | ## v21 (3/21/2012)
260 |
261 | Features:
262 |
263 | * bundler 1.1.2
264 |
265 | ## v20 (3/12/2012)
266 |
267 | Features:
268 |
269 | * bundler 1.1.0 \o/
270 |
271 | ## v19 (1/25/2012)
272 |
273 | Bugfixes:
274 |
275 | * fix native extension building for rbx 2.0.0dev
276 |
277 | ## v18 (1/18/2012)
278 |
279 | Features:
280 |
281 | * JRuby support
282 | * rbx 2.0.0dev support
283 |
284 | Bugfixes:
285 |
286 | * force db password to be a string in the yaml file
287 |
288 | ## v17 (12/29/2011)
289 |
290 | Features:
291 |
292 | * bundler 1.1.rc.7
293 |
294 | ## v16 (12/29/2011)
295 |
296 | Features:
297 |
298 | * pass DATABASE_URL to rails 3.1 assets:precompile rake task detection
299 |
300 | ## v15 (12/27/2011)
301 |
302 | Features:
303 |
304 | * bundler 1.1.rc.6
305 |
306 | ## v14 (12/22/2011)
307 |
308 | Bugfixes:
309 |
310 | * stop freedom patching syck in ruby 1.9.3+
311 |
312 | ## v13 (12/15/2011)
313 |
314 | Features:
315 |
316 | * bundler 1.1.rc.5
317 |
318 | ## v12 (12/13/2011)
319 |
320 | Bugfixes:
321 |
322 | * syck workaround for yaml/psych issues
323 |
324 | ## v11 (12/12/2011)
325 |
326 | Features:
327 |
328 | * bundler 1.1.rc.3
329 |
330 | ## v10 (11/23/2011)
331 |
332 | Features:
333 |
334 | * bundler binstubs
335 | * dynamic slug_vendor_base detection
336 |
337 | Bugfixes:
338 |
339 | * don't show sqlite3 error if it's in a bundle without group on failed bundle install
340 |
341 | ## v9 (11/14/2011)
342 |
343 | Features:
344 |
345 | * rbx 1.2.4 support
346 | * print out RUBY_VERSION being used
347 |
348 | Bugfixes:
349 |
350 | * don't leave behind ruby_versions.yml
351 |
352 | ## v8 (11/8/2011)
353 |
354 | Features:
355 |
356 | * use vm as part of RUBY_VERSION
357 |
358 | ## v7 (11/8/2011)
359 |
360 | Features:
361 |
362 | * ruby 1.9.3 support
363 | * specify ruby versions using RUBY_VERSION build var
364 |
365 | Bugfixes:
366 |
367 | * move "bin/" to the front of the PATH, so apps can override existing bins
368 |
369 | ## v6 (11/2/2011)
370 |
371 | Features:
372 |
373 | * add sqlite3 warning when detected on bundle install error
374 |
375 | Bugfixes:
376 |
377 | * Change gem detection to use lockfile parser
378 | * use `$RACK_ENV` when thin is detected for rack apps
379 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "fileutils"
2 | require "tmpdir"
3 |
4 | S3_BUCKET_NAME = "heroku-buildpack-ruby"
5 | VENDOR_URL = "https://s3.amazonaws.com/#{S3_BUCKET_NAME}"
6 |
7 | def s3_tools_dir
8 | File.expand_path("../support/s3", __FILE__)
9 | end
10 |
11 | def s3_upload(tmpdir, name)
12 | sh("#{s3_tools_dir}/s3 put #{S3_BUCKET_NAME} #{name}.tgz #{tmpdir}/#{name}.tgz")
13 | end
14 |
15 | def vendor_plugin(git_url, branch = nil)
16 | name = File.basename(git_url, File.extname(git_url))
17 | Dir.mktmpdir("#{name}-") do |tmpdir|
18 | FileUtils.rm_rf("#{tmpdir}/*")
19 |
20 | Dir.chdir(tmpdir) do
21 | sh "git clone #{git_url} ."
22 | sh "git checkout origin/#{branch}" if branch
23 | FileUtils.rm_rf("#{name}/.git")
24 | sh("tar czvf #{tmpdir}/#{name}.tgz *")
25 | s3_upload(tmpdir, name)
26 | end
27 | end
28 | end
29 |
30 | def in_gem_env(gem_home, &block)
31 | old_gem_home = ENV['GEM_HOME']
32 | old_gem_path = ENV['GEM_PATH']
33 | ENV['GEM_HOME'] = ENV['GEM_PATH'] = gem_home.to_s
34 |
35 | yield
36 |
37 | ENV['GEM_HOME'] = old_gem_home
38 | ENV['GEM_PATH'] = old_gem_path
39 | end
40 |
41 | def install_gem(gem, version)
42 | name = "#{gem}-#{version}"
43 | Dir.mktmpdir("#{gem}-#{version}") do |tmpdir|
44 | Dir.chdir(tmpdir) do |dir|
45 | FileUtils.rm_rf("#{tmpdir}/*")
46 |
47 | in_gem_env(tmpdir) do
48 | sh("gem install #{gem} --version #{version} --no-ri --no-rdoc --env-shebang")
49 | sh("tar czvf #{tmpdir}/#{name}.tgz *")
50 | s3_upload(tmpdir, name)
51 | end
52 | end
53 | end
54 | end
55 |
56 | def build_ruby_command(name, output, prefix, usr_dir, tmpdir, rubygems = nil)
57 | vulcan_prefix = "/app/vendor/#{output}"
58 | build_command = [
59 | # need to move libyaml/libffi to dirs we can see
60 | "mv #{usr_dir} /tmp",
61 | "./configure --enable-load-relative --disable-install-doc --prefix #{prefix}",
62 | "env CPATH=/tmp/#{usr_dir}/include:\\$CPATH CPPATH=/tmp/#{usr_dir}/include:\\$CPPATH LIBRARY_PATH=/tmp/#{usr_dir}/lib:\\$LIBRARY_PATH make",
63 | "make install"
64 | ]
65 | build_command << "#{prefix}/bin/ruby /tmp/#{usr_dir}/rubygems-#{rubygems}/setup.rb" if rubygems
66 | build_command << "mv #{prefix} /app/vendor/#{output}" if prefix != "/app/vendor/#{output}"
67 | build_command = build_command.join(" && ")
68 |
69 | sh "vulcan build -v -o #{output}.tgz --prefix #{vulcan_prefix} --source #{name} --command=\"#{build_command}\""
70 | s3_upload(tmpdir, output)
71 | end
72 |
73 | def build_rbx_command(name, output, prefix, usr_dir, tmpdir, ruby_version)
74 | build_command = [
75 | # need to move libyaml/libffi to dirs we can see
76 | "mv usr /tmp",
77 | "ls /tmp/#{usr_dir}",
78 | "./configure --prefix #{prefix} --enable-version=#{ruby_version} --default-version=#{ruby_version} --with-include-dir=/tmp/#{usr_dir}/include --with-lib-dir=/tmp/#{usr_dir}/lib",
79 | "rake install"
80 | ]
81 | # build_command << "mv #{prefix} /app/vendor/#{name}" if name != output
82 | build_command = build_command.join(" && ")
83 |
84 | sh "vulcan build -v -o #{output}.tgz --source #{name} --prefix #{prefix} --command=\"#{build_command}\""
85 | s3_upload(tmpdir, output)
86 | end
87 |
88 | desc "update plugins"
89 | task "plugins:update" do
90 | vendor_plugin "http://github.com/heroku/rails_log_stdout.git", "legacy"
91 | vendor_plugin "http://github.com/pedro/rails3_serve_static_assets.git"
92 | vendor_plugin "http://github.com/hone/rails31_enable_runtime_asset_compilation.git"
93 | end
94 |
95 | desc "install vendored gem"
96 | task "gem:install", :gem, :version do |t, args|
97 | gem = args[:gem]
98 | version = args[:version]
99 |
100 | install_gem(gem, version)
101 | end
102 |
103 | desc "install libyaml"
104 | task "libyaml:install", :version do |t, args|
105 | version = args[:version]
106 | name = "libyaml-#{version}"
107 | Dir.mktmpdir("libyaml-") do |tmpdir|
108 | Dir.chdir(tmpdir) do |dir|
109 | FileUtils.rm_rf("#{tmpdir}/*")
110 | prefix = "/app/vendor/yaml-#{version}"
111 |
112 | sh "curl http://pyyaml.org/download/libyaml/yaml-#{version}.tar.gz -s -o - | tar vzxf -"
113 |
114 | build_command = [
115 | "env CFLAGS=-fPIC ./configure --enable-static --disable-shared --prefix=#{prefix}",
116 | "make",
117 | "make install"
118 | ].join(" && ")
119 |
120 | sh "vulcan build -v -o #{name}.tgz --source yaml-#{version} --prefix=#{prefix} --command=\"#{build_command}\""
121 | s3_upload(tmpdir, name)
122 | end
123 | end
124 | end
125 |
126 | desc "install node"
127 | task "node:install", :version do |t, args|
128 | version = args[:version]
129 | name = "node-#{version}"
130 | prefix = "/app/vendor/node-v#{version}"
131 | Dir.mktmpdir("node-") do |tmpdir|
132 | Dir.chdir(tmpdir) do |dir|
133 | FileUtils.rm_rf("#{tmpdir}/*")
134 |
135 | sh "curl http://nodejs.org/dist/node-v#{version}.tar.gz -s -o - | tar vzxf -"
136 |
137 | build_command = [
138 | "./configure --prefix #{prefix}",
139 | "make install",
140 | "mv #{prefix}/bin/node #{prefix}/.",
141 | "rm -rf #{prefix}/include",
142 | "rm -rf #{prefix}/lib",
143 | "rm -rf #{prefix}/share",
144 | "rm -rf #{prefix}/bin"
145 | ].join(" && ")
146 |
147 | sh "vulcan build -v -o #{name}.tgz --source node-v#{version} --command=\"#{build_command}\""
148 | s3_upload(tmpdir, name)
149 | end
150 | end
151 | end
152 |
153 | desc "install ruby"
154 | task "ruby:install", :version do |t, args|
155 | full_version = args[:version]
156 | full_name = "ruby-#{full_version}"
157 | version = full_version.split('-').first
158 | name = "ruby-#{version}"
159 | usr_dir = "usr"
160 | rubygems = nil
161 | Dir.mktmpdir("ruby-") do |tmpdir|
162 | Dir.chdir(tmpdir) do |dir|
163 | FileUtils.rm_rf("#{tmpdir}/*")
164 |
165 | major_ruby = version.match(/\d\.\d/)[0]
166 | rubygems = "1.8.24" if major_ruby == "1.8"
167 | sh "curl http://ftp.ruby-lang.org/pub/ruby/#{major_ruby}/#{full_name}.tar.gz -s -o - | tar zxf -"
168 | FileUtils.mkdir_p("#{full_name}/#{usr_dir}")
169 | Dir.chdir("#{full_name}/#{usr_dir}") do
170 | sh "curl #{VENDOR_URL}/libyaml-0.1.4.tgz -s -o - | tar zxf -"
171 | sh "curl #{VENDOR_URL}/libffi-3.0.10.tgz -s -o - | tar zxf -"
172 | sh "curl http://production.cf.rubygems.org/rubygems/rubygems-#{rubygems}.tgz -s -o - | tar xzf -" if major_ruby == "1.8"
173 | end
174 |
175 | # runtime ruby
176 | prefix = "/app/vendor/#{name}"
177 | build_ruby_command(full_name, name, prefix, usr_dir, tmpdir, rubygems)
178 |
179 | # build ruby
180 | if major_ruby == "1.8"
181 | output = "ruby-build-#{version}"
182 | prefix = "/tmp/ruby-#{version}"
183 | build_ruby_command(full_name, output, prefix, usr_dir, tmpdir, rubygems)
184 | end
185 | end
186 | end
187 | end
188 |
189 | desc "install rbx"
190 | task "rbx:install", :version do |t, args|
191 | version = args[:version]
192 | name = "rubinius-#{version}"
193 | output = "rbx-#{version}"
194 | prefix = "/app/vendor/#{output}"
195 |
196 | Dir.mktmpdir("rbx-") do |tmpdir|
197 | Dir.chdir(tmpdir) do |dir|
198 | FileUtils.rm_rf("#{tmpdir}/*")
199 |
200 | sh "curl http://asset.rubini.us/#{name}.tar.gz -s -o - | tar vzxf -"
201 | build_command = [
202 | "./configure --prefix #{prefix}",
203 | "rake install"
204 | ].join(" && ")
205 |
206 | sh "vulcan build -v -o #{output}.tgz --source #{name} --prefix #{prefix} --command=\"#{build_command}\""
207 | s3_upload(tmpdir, output)
208 | end
209 | end
210 | end
211 |
212 | desc "install rbx 2.0.0dev"
213 | task "rbx2dev:install", :version, :ruby_version do |t, args|
214 | version = args[:version]
215 | ruby_version = args[:ruby_version]
216 | source = "rubinius-#{version}"
217 | name = "rubinius-2.0.0dev"
218 | output = "rbx-#{version}-#{ruby_version}"
219 | usr_dir = "usr"
220 |
221 | Dir.mktmpdir("rbx-") do |tmpdir|
222 | Dir.chdir(tmpdir) do |dir|
223 | FileUtils.rm_rf("#{tmpdir}/*")
224 |
225 | sh "curl http://asset.rubini.us/#{source}.tar.gz -s -o - | tar vzxf -"
226 | FileUtils.mkdir_p("#{name}/#{usr_dir}")
227 | Dir.chdir("#{name}/#{usr_dir}") do
228 | sh "curl #{VENDOR_URL}/libyaml-0.1.4.tgz -s -o - | tar vzxf -"
229 | sh "curl #{VENDOR_URL}/libffi-3.0.10.tgz -s -o - | tar vzxf -"
230 | end
231 |
232 | prefix = "/app/vendor/#{output}"
233 | build_rbx_command(name, output, prefix, usr_dir, tmpdir, ruby_version)
234 |
235 | # rbx build
236 | prefix = "/tmp/#{output}"
237 | output = "rbx-build-#{version}-#{ruby_version}"
238 | build_rbx_command(name, output, prefix, usr_dir, tmpdir, ruby_version)
239 | end
240 | end
241 | end
242 |
243 | desc "install jruby"
244 | task "jruby:install", :version, :ruby_version do |t, args|
245 | version = args[:version]
246 | ruby_version = args[:ruby_version]
247 | name = "jruby-src-#{version}"
248 | src_folder = "jruby-#{version}"
249 | output = "ruby-#{ruby_version}-jruby-#{version}"
250 | launcher = "launcher"
251 |
252 | Dir.mktmpdir("jruby-") do |tmpdir|
253 | Dir.chdir(tmpdir) do
254 | sh "curl http://jruby.org.s3.amazonaws.com/downloads/#{version}/#{name}.tar.gz -s -o - | tar vzxf -"
255 | sh "rm -rf test"
256 | Dir.chdir(src_folder) do
257 | sh "curl http://www.nic.funet.fi/pub/mirrors/apache.org/ant/binaries/apache-ant-1.8.4-bin.tar.gz -s -o - | tar vxzf -"
258 | sh "rm -rf manual"
259 | end
260 | Dir.chdir("#{src_folder}/bin") do
261 | sh "curl #{VENDOR_URL}/jruby-launcher-1.0.12-java.tgz -s -o - | tar vzxf -"
262 | end
263 |
264 | major, minor, patch = ruby_version.split('.')
265 |
266 | build_command = [
267 | "apache-ant-1.8.4/bin/ant -Djruby.default.ruby.version=#{major}.#{minor}",
268 | "rm bin/*.bat",
269 | "rm bin/*.dll",
270 | "rm bin/*.exe",
271 | "ln -s jruby bin/ruby",
272 | "mkdir -p /app/vendor/#{output}",
273 | "mv bin /app/vendor/#{output}",
274 | "mv lib /app/vendor/#{output}"
275 | ]
276 | build_command = build_command.join(" && ")
277 | sh "vulcan build -v -o #{output}.tgz --prefix /app/vendor/#{output} --source #{src_folder} --command=\"#{build_command}\""
278 |
279 | s3_upload(tmpdir, output)
280 | end
281 | end
282 | end
283 |
284 | desc "build the jruby-launcher"
285 | task "jruby:launcher", :version do |t, args|
286 | version = args[:version]
287 | name = "jruby-launcher-#{version}-java"
288 | prefix = "/tmp/jruby-launcher"
289 |
290 | Dir.mktmpdir("jruby-launcher-") do |tmpdir|
291 | Dir.chdir(tmpdir) do
292 | sh "gem fetch jruby-launcher --platform java --version #{version}"
293 | sh "gem unpack jruby-launcher-#{version}-java.gem"
294 |
295 | build_command = [
296 | "make",
297 | "mkdir -p #{prefix}",
298 | "cp jruby #{prefix}"
299 | ].join(" && ")
300 |
301 | sh "vulcan build -v -o #{name}.tgz --source #{name} --prefix #{prefix} --command=\"#{build_command}\""
302 | s3_upload(tmpdir, name)
303 | end
304 | end
305 |
306 | end
307 |
308 | desc "generate ruby versions manifest"
309 | task "ruby:manifest" do
310 | require 'rexml/document'
311 | require 'yaml'
312 |
313 | document = REXML::Document.new(`curl https://#{S3_BUCKET_NAME}.s3.amazonaws.com`)
314 | rubies = document.elements.to_a("//Contents/Key").map {|node| node.text }.select {|text| text.match(/^(ruby|rbx|jruby)-\\\\d+\\\\.\\\\d+\\\\.\\\\d+(-p\\\\d+)?/) }
315 |
316 | Dir.mktmpdir("ruby_versions-") do |tmpdir|
317 | name = 'ruby_versions.yml'
318 | File.open(name, 'w') {|file| file.puts(rubies.to_yaml) }
319 | sh("#{s3_tools_dir}/s3 put #{S3_BUCKET_NAME} #{name} #{name}")
320 | end
321 | end
322 |
323 | desc "install libffi"
324 | task "libffi:install", :version do |t, args|
325 | version = args[:version]
326 | name = "libffi-#{version}"
327 | prefix = "/app/vendor/#{name}"
328 | Dir.mktmpdir("libffi-") do |tmpdir|
329 | Dir.chdir(tmpdir) do |dir|
330 | FileUtils.rm_rf("#{tmpdir}/*")
331 |
332 | sh "curl ftp://sourceware.org/pub/libffi/libffi-#{version}.tar.gz -s -o - | tar vzxf -"
333 |
334 | build_command = [
335 | "env CFLAGS=-fPIC ./configure --enable-static --disable-shared --prefix=#{prefix}",
336 | "make",
337 | "make install",
338 | "mv #{prefix}/lib/#{name}/include #{prefix}",
339 | "rm -rf #{prefix}/lib/#{name}"
340 | ].join(" && ")
341 |
342 | sh "vulcan build -v -o #{name}.tgz --source #{name} --prefix=#{prefix} --command=\"#{build_command}\""
343 | s3_upload(tmpdir, name)
344 | end
345 | end
346 | end
347 |
348 | begin
349 | require 'rspec/core/rake_task'
350 |
351 | desc "Run specs"
352 | RSpec::Core::RakeTask.new(:spec) do |t|
353 | t.rspec_opts = %w(-fs --color)
354 | #t.ruby_opts = %w(-w)
355 | end
356 | task :default => :spec
357 | rescue LoadError => e
358 | end
359 |
--------------------------------------------------------------------------------
/lib/language_pack/ruby.rb:
--------------------------------------------------------------------------------
1 | require "tmpdir"
2 | require "rubygems"
3 | require "language_pack"
4 | require "language_pack/base"
5 | require "language_pack/bundler_lockfile"
6 |
7 | # base Ruby Language Pack. This is for any base ruby app.
8 | class LanguagePack::Ruby < LanguagePack::Base
9 | include LanguagePack::BundlerLockfile
10 | extend LanguagePack::BundlerLockfile
11 |
12 | BUILDPACK_VERSION = "v61"
13 | LIBYAML_VERSION = "0.1.4"
14 | LIBYAML_PATH = "libyaml-#{LIBYAML_VERSION}"
15 | BUNDLER_VERSION = "1.3.2"
16 | BUNDLER_GEM_PATH = "bundler-#{BUNDLER_VERSION}"
17 | NODE_VERSION = "0.4.7"
18 | NODE_JS_BINARY_PATH = "node-#{NODE_VERSION}"
19 | JVM_BASE_URL = "http://heroku-jdk.s3.amazonaws.com"
20 | JVM_VERSION = "openjdk7-latest"
21 | SQLITE_VERSION = "3071700"
22 | SQLITE_PATH = "sqlite-autoconf-#{SQLITE_VERSION}"
23 |
24 | MY_VENDOR_URL = "https://s3.amazonaws.com/prod-audi-leasing-us"
25 |
26 | # detects if this is a valid Ruby app
27 | # @return [Boolean] true if it's a Ruby app
28 | def self.use?
29 | File.exist?("Gemfile")
30 | end
31 |
32 | def self.lockfile_parser
33 | require "bundler"
34 | Bundler::LockfileParser.new(File.read("Gemfile.lock"))
35 | end
36 |
37 | def self.gem_version(name)
38 | gem_version = nil
39 | bootstrap_bundler do |bundler_path|
40 | $: << "#{bundler_path}/gems/bundler-#{LanguagePack::Ruby::BUNDLER_VERSION}/lib"
41 | gem = lockfile_parser.specs.detect { |gem| gem.name == name }
42 | gem_version = gem.version if gem
43 | end
44 |
45 | gem_version
46 | end
47 |
48 | def name
49 | "Ruby"
50 | end
51 |
52 | def default_addons
53 | add_dev_database_addon
54 | end
55 |
56 | def default_config_vars
57 | vars = {
58 | "LANG" => "en_US.UTF-8",
59 | "PATH" => default_path,
60 | "GEM_PATH" => slug_vendor_base,
61 | }
62 |
63 | ruby_version_jruby? ? vars.merge({
64 | "JAVA_OPTS" => default_java_opts,
65 | "JRUBY_OPTS" => default_jruby_opts,
66 | "JAVA_TOOL_OPTIONS" => default_java_tool_options
67 | }) : vars
68 | end
69 |
70 | def default_process_types
71 | {
72 | "rake" => "bundle exec rake",
73 | "console" => "bundle exec irb"
74 | }
75 | end
76 |
77 | def compile
78 | Dir.chdir(build_path)
79 | remove_vendor_bundle
80 | install_ruby
81 | install_jvm
82 | setup_language_pack_environment
83 | setup_profiled
84 | allow_git do
85 | install_language_pack_gems
86 | build_bundler
87 | create_database_yml
88 | install_binaries
89 | run_assets_precompile_rake_task
90 | end
91 | end
92 |
93 | private
94 |
95 | # the base PATH environment variable to be used
96 | # @return [String] the resulting PATH
97 | def default_path
98 | "bin:#{slug_vendor_base}/bin:/usr/local/bin:/usr/bin:/bin"
99 | end
100 |
101 | # the relative path to the bundler directory of gems
102 | # @return [String] resulting path
103 | def slug_vendor_base
104 | if @slug_vendor_base
105 | @slug_vendor_base
106 | elsif @ruby_version == "ruby-1.8.7"
107 | @slug_vendor_base = "vendor/bundle/1.8"
108 | else
109 | @slug_vendor_base = run(%q(ruby -e "require 'rbconfig';puts \"vendor/bundle/#{RUBY_ENGINE}/#{RbConfig::CONFIG['ruby_version']}\"")).chomp
110 | end
111 | end
112 |
113 | # the relative path to the vendored ruby directory
114 | # @return [String] resulting path
115 | def slug_vendor_ruby
116 | "vendor/#{ruby_version}"
117 | end
118 |
119 | # the relative path to the vendored jvm
120 | # @return [String] resulting path
121 | def slug_vendor_jvm
122 | "vendor/jvm"
123 | end
124 |
125 | # the absolute path of the build ruby to use during the buildpack
126 | # @return [String] resulting path
127 | def build_ruby_path
128 | "/tmp/#{ruby_version}"
129 | end
130 |
131 | # fetch the ruby version from bundler
132 | # @return [String, nil] returns the ruby version if detected or nil if none is detected
133 | def ruby_version
134 | return @ruby_version if @ruby_version_run
135 |
136 | @ruby_version_run = true
137 |
138 | bootstrap_bundler do |bundler_path|
139 | old_system_path = "/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin"
140 | @ruby_version = run_stdout("env PATH=#{old_system_path}:#{bundler_path}/bin GEM_PATH=#{bundler_path} bundle platform --ruby").chomp
141 | end
142 |
143 | if @ruby_version == "No ruby version specified" && ENV['RUBY_VERSION']
144 | # for backwards compatibility.
145 | # this will go away in the future
146 | @ruby_version = ENV['RUBY_VERSION']
147 | @ruby_version_env_var = true
148 | elsif @ruby_version == "No ruby version specified"
149 | @ruby_version = nil
150 | else
151 | @ruby_version = @ruby_version.sub('(', '').sub(')', '').split.join('-')
152 | @ruby_version_env_var = false
153 | end
154 |
155 | @ruby_version
156 | end
157 |
158 | # determine if we're using rbx
159 | # @return [Boolean] true if we are and false if we aren't
160 | def ruby_version_rbx?
161 | ruby_version ? ruby_version.match(/rbx-/) : false
162 | end
163 |
164 | # determine if we're using jruby
165 | # @return [Boolean] true if we are and false if we aren't
166 | def ruby_version_jruby?
167 | @ruby_version_jruby ||= ruby_version ? ruby_version.match(/jruby-/) : false
168 | end
169 |
170 | # default JAVA_OPTS
171 | # return [String] string of JAVA_OPTS
172 | def default_java_opts
173 | "-Xmx384m -Xss512k -XX:+UseCompressedOops -Dfile.encoding=UTF-8"
174 | end
175 |
176 | # default JRUBY_OPTS
177 | # return [String] string of JRUBY_OPTS
178 | def default_jruby_opts
179 | "-Xcompile.invokedynamic=true"
180 | end
181 |
182 | # default JAVA_TOOL_OPTIONS
183 | # return [String] string of JAVA_TOOL_OPTIONS
184 | def default_java_tool_options
185 | "-Djava.rmi.server.useCodebaseOnly=true"
186 | end
187 |
188 | # list the available valid ruby versions
189 | # @note the value is memoized
190 | # @return [Array] list of Strings of the ruby versions available
191 | def ruby_versions
192 | return @ruby_versions if @ruby_versions
193 |
194 | Dir.mktmpdir("ruby_versions-") do |tmpdir|
195 | Dir.chdir(tmpdir) do
196 | run("curl -O #{VENDOR_URL}/ruby_versions.yml")
197 | @ruby_versions = YAML::load_file("ruby_versions.yml")
198 | end
199 | end
200 |
201 | @ruby_versions
202 | end
203 |
204 | # sets up the environment variables for the build process
205 | def setup_language_pack_environment
206 | setup_ruby_install_env
207 |
208 | config_vars = default_config_vars.each do |key, value|
209 | ENV[key] ||= value
210 | end
211 | ENV["GEM_HOME"] = slug_vendor_base
212 | ENV["PATH"] = "#{ruby_install_binstub_path}:#{config_vars["PATH"]}"
213 | end
214 |
215 | # sets up the profile.d script for this buildpack
216 | def setup_profiled
217 | set_env_override "GEM_PATH", "$HOME/#{slug_vendor_base}:$GEM_PATH"
218 | set_env_default "LANG", "en_US.UTF-8"
219 | set_env_override "PATH", "$HOME/bin:$HOME/#{slug_vendor_base}/bin:$PATH"
220 |
221 | if ruby_version_jruby?
222 | set_env_default "JAVA_OPTS", default_java_opts
223 | set_env_default "JRUBY_OPTS", default_jruby_opts
224 | set_env_default "JAVA_TOOL_OPTIONS", default_java_tool_options
225 | end
226 | end
227 |
228 | # determines if a build ruby is required
229 | # @return [Boolean] true if a build ruby is required
230 | def build_ruby?
231 | @build_ruby ||= !ruby_version_rbx? && !ruby_version_jruby? && !%w{ruby-1.9.3 ruby-2.0.0}.include?(ruby_version)
232 | end
233 |
234 | # install the vendored ruby
235 | # @return [Boolean] true if it installs the vendored ruby and false otherwise
236 | def install_ruby
237 | return false unless ruby_version
238 |
239 | invalid_ruby_version_message = < true
360 | end
361 |
362 | # install libyaml into the LP to be referenced for psych compilation
363 | # @param [String] tmpdir to store the libyaml files
364 | def install_libyaml(dir)
365 | FileUtils.mkdir_p dir
366 | Dir.chdir(dir) do |dir|
367 | run("curl #{VENDOR_URL}/#{LIBYAML_PATH}.tgz -s -o - | tar xzf -")
368 | end
369 | end
370 |
371 | # install sqlite into the LP to be referenced for psych compilation
372 | # @param [String] tmpdir to store the sqlite files
373 | def install_sqlite(dir)
374 | FileUtils.mkdir_p dir
375 | Dir.chdir(dir) do |dir|
376 | puts "curl #{MY_VENDOR_URL}/#{SQLITE_PATH}.tar.gz -s -o - | tar xzf - 2>&1"
377 | run("curl #{MY_VENDOR_URL}/#{SQLITE_PATH}.tar.gz -s -o - | tar xzf - 2>&1")
378 | end
379 | end
380 |
381 | # remove `vendor/bundle` that comes from the git repo
382 | # in case there are native ext.
383 | # users should be using `bundle pack` instead.
384 | # https://github.com/heroku/heroku-buildpack-ruby/issues/21
385 | def remove_vendor_bundle
386 | if File.exists?("vendor/bundle")
387 | topic "WARNING: Removing `vendor/bundle`."
388 | puts "Checking in `vendor/bundle` is not supported. Please remove this directory"
389 | puts "and add it to your .gitignore. To vendor your gems with Bundler, use"
390 | puts "`bundle pack` instead."
391 | FileUtils.rm_rf("vendor/bundle")
392 | end
393 | end
394 |
395 | # runs bundler to install the dependencies
396 | def build_bundler
397 | log("bundle") do
398 | bundle_without = ENV["BUNDLE_WITHOUT"] || "development:test"
399 | bundle_command = "bundle install --without #{bundle_without} --path vendor/bundle --binstubs vendor/bundle/bin"
400 |
401 | unless File.exist?("Gemfile.lock")
402 | error "Gemfile.lock is required. Please run \"bundle install\" locally\nand commit your Gemfile.lock."
403 | end
404 |
405 | if has_windows_gemfile_lock?
406 | topic "WARNING: Removing `Gemfile.lock` because it was generated on Windows."
407 | puts "Bundler will do a full resolve so native gems are handled properly."
408 | puts "This may result in unexpected gem versions being used in your app."
409 |
410 | log("bundle", "has_windows_gemfile_lock")
411 | File.unlink("Gemfile.lock")
412 | else
413 | # using --deployment is preferred if we can
414 | bundle_command += " --deployment"
415 | cache_load ".bundle"
416 | end
417 |
418 | version = run_stdout("bundle version").strip
419 | topic("Installing dependencies using #{version}")
420 |
421 | load_bundler_cache
422 |
423 | bundler_output = ""
424 | Dir.mktmpdir("yamltag-") do |tmpdir|
425 | libyaml_dir = "#{tmpdir}/#{LIBYAML_PATH}"
426 | sqlite_dir = "#{tmpdir}/#{SQLITE_PATH}"
427 | puts "Installing libyaml to #{libyaml_dir}"
428 | install_libyaml(libyaml_dir)
429 |
430 | puts "Installing SQLite to #{sqlite_dir}"
431 | install_sqlite(sqlite_dir)
432 |
433 | if File.exist? "#{sqlite_dir}/#{SQLITE_PATH}/"
434 | puts "folder exists"
435 | else
436 | puts "folder missing"
437 | end
438 |
439 | if File.exist? "#{sqlite_dir}/#{SQLITE_PATH}/sqlite3.h"
440 | puts "sqlite3.h exists"
441 | else
442 | puts "sqlite3.h missing"
443 | end
444 |
445 | # need to setup compile environment for the psych gem
446 | yaml_include = File.expand_path("#{libyaml_dir}/include")
447 | yaml_lib = File.expand_path("#{libyaml_dir}/lib")
448 |
449 | sqlite_include = File.expand_path("#{sqlite_dir}/include")
450 | sqlite_lib = File.expand_path("#{sqlite_dir}/lib")
451 |
452 |
453 | pwd = run("pwd").chomp
454 | bundler_path = "#{pwd}/#{slug_vendor_base}/gems/#{BUNDLER_GEM_PATH}/lib"
455 | # we need to set BUNDLE_CONFIG and BUNDLE_GEMFILE for
456 | # codon since it uses bundler.
457 |
458 | env_vars = "env BUNDLE_GEMFILE=#{pwd}/Gemfile BUNDLE_CONFIG=#{pwd}/.bundle/config CPATH=#{yaml_include}:#{sqlite_include}:$CPATH CPPATH=#{yaml_include}:#{sqlite_include}:$CPPATH LIBRARY_PATH=#{yaml_lib}:#{sqlite_lib}:$LIBRARY_PATH RUBYOPT=\"#{syck_hack}\""
459 | env_vars += " BUNDLER_LIB_PATH=#{bundler_path}" if ruby_version == "ruby-1.8.7"
460 |
461 | sqlite_command = "gem install sqlite3 -- --with-sqlite3-dir=#{sqlite_dir}/#{SQLITE_PATH}/"
462 | puts "Running: #{sqlite_command}"
463 | bundler_output << pipe(sqlite_command)
464 |
465 | puts "Running: #{bundle_command}"
466 | bundler_output << pipe("#{env_vars} #{bundle_command} --no-clean 2>&1")
467 | end
468 |
469 | if $?.success?
470 | log "bundle", :status => "success"
471 | puts "Cleaning up the bundler cache."
472 | pipe "bundle clean 2> /dev/null"
473 | cache_store ".bundle"
474 | cache_store "vendor/bundle"
475 |
476 | # Keep gem cache out of the slug
477 | FileUtils.rm_rf("#{slug_vendor_base}/cache")
478 |
479 | # symlink binstubs
480 | bin_dir = "bin"
481 | FileUtils.mkdir_p bin_dir
482 | Dir["#{slug_vendor_base}/bin/*"].each do |bin|
483 | run("ln -s ../#{bin} #{bin_dir}") unless File.exist?("#{bin_dir}/#{bin}")
484 | end
485 | else
486 | log "bundle", :status => "failure"
487 | error_message = "Failed to install gems via Bundler."
488 | # if bundler_output.match(/Installing sqlite3 \([\w.]+\) with native extensions\s+Gem::Installer::ExtensionBuildError: ERROR: Failed to build gem native extension./)
489 | # error_message += <
563 |
564 | <%= ENV["RAILS_ENV"] || ENV["RACK_ENV"] %>:
565 | <%= attribute "adapter", adapter %>
566 | <%= attribute "database", database %>
567 | <%= attribute "username", username %>
568 | <%= attribute "password", password, true %>
569 | <%= attribute "host", host %>
570 | <%= attribute "port", port %>
571 |
572 | <% params.each do |key, value| %>
573 | <%= key %>: <%= value.first %>
574 | <% end %>
575 | DATABASE_YML
576 | end
577 | end
578 | end
579 |
580 | # add bundler to the load path
581 | # @note it sets a flag, so the path can only be loaded once
582 | def add_bundler_to_load_path
583 | return if @bundler_loadpath
584 | $: << File.expand_path(Dir["#{slug_vendor_base}/gems/bundler*/lib"].first)
585 | @bundler_loadpath = true
586 | end
587 |
588 | # detects whether the Gemfile.lock contains the Windows platform
589 | # @return [Boolean] true if the Gemfile.lock was created on Windows
590 | def has_windows_gemfile_lock?
591 | lockfile_parser.platforms.detect do |platform|
592 | /mingw|mswin/.match(platform.os) if platform.is_a?(Gem::Platform)
593 | end
594 | end
595 |
596 | # detects if a gem is in the bundle.
597 | # @param [String] name of the gem in question
598 | # @return [String, nil] if it finds the gem, it will return the line from bundle show or nil if nothing is found.
599 | def gem_is_bundled?(gem)
600 | @bundler_gems ||= lockfile_parser.specs.map(&:name)
601 | @bundler_gems.include?(gem)
602 | end
603 |
604 | # setup the lockfile parser
605 | # @return [Bundler::LockfileParser] a Bundler::LockfileParser
606 | def lockfile_parser
607 | add_bundler_to_load_path
608 | @lockfile_parser ||= LanguagePack::Ruby.lockfile_parser
609 | end
610 |
611 | # detects if a rake task is defined in the app
612 | # @param [String] the task in question
613 | # @return [Boolean] true if the rake task is defined in the app
614 | def rake_task_defined?(task)
615 | run("env PATH=$PATH bundle exec rake #{task} --dry-run") && $?.success?
616 | end
617 |
618 | # executes the block with GIT_DIR environment variable removed since it can mess with the current working directory git thinks it's in
619 | # @param [block] block to be executed in the GIT_DIR free context
620 | def allow_git(&blk)
621 | git_dir = ENV.delete("GIT_DIR") # can mess with bundler
622 | blk.call
623 | ENV["GIT_DIR"] = git_dir
624 | end
625 |
626 | # decides if we need to enable the dev database addon
627 | # @return [Array] the database addon if the pg gem is detected or an empty Array if it isn't.
628 | def add_dev_database_addon
629 | gem_is_bundled?("pg") ? ['heroku-postgresql:dev'] : []
630 | end
631 |
632 | # decides if we need to install the node.js binary
633 | # @note execjs will blow up if no JS RUNTIME is detected and is loaded.
634 | # @return [Array] the node.js binary path if we need it or an empty Array
635 | def add_node_js_binary
636 | gem_is_bundled?('execjs') ? [NODE_JS_BINARY_PATH] : []
637 | end
638 |
639 | def run_assets_precompile_rake_task
640 | if rake_task_defined?("assets:precompile")
641 | require 'benchmark'
642 |
643 | topic "Running: rake assets:precompile"
644 | time = Benchmark.realtime { pipe("env PATH=$PATH:bin bundle exec rake assets:precompile 2>&1") }
645 | if $?.success?
646 | puts "Asset precompilation completed (#{"%.2f" % time}s)"
647 | end
648 | end
649 | end
650 |
651 | def bundler_cache
652 | "vendor/bundle"
653 | end
654 |
655 | def load_bundler_cache
656 | cache_load "vendor"
657 |
658 | full_ruby_version = run_stdout(%q(ruby -v)).chomp
659 | rubygems_version = run_stdout(%q(gem -v)).chomp
660 | heroku_metadata = "vendor/heroku"
661 | old_rubygems_version = nil
662 | ruby_version_cache = "#{heroku_metadata}/ruby_version"
663 | buildpack_version_cache = "#{heroku_metadata}/buildpack_version"
664 | bundler_version_cache = "#{heroku_metadata}/bundler_version"
665 | rubygems_version_cache = "#{heroku_metadata}/rubygems_version"
666 |
667 | old_rubygems_version = File.read(rubygems_version_cache).chomp if File.exists?(rubygems_version_cache)
668 |
669 | # fix bug from v37 deploy
670 | if File.exists?("vendor/ruby_version")
671 | puts "Broken cache detected. Purging build cache."
672 | cache_clear("vendor")
673 | FileUtils.rm_rf("vendor/ruby_version")
674 | purge_bundler_cache
675 | # fix bug introduced in v38
676 | elsif !File.exists?(buildpack_version_cache) && File.exists?(ruby_version_cache)
677 | puts "Broken cache detected. Purging build cache."
678 | purge_bundler_cache
679 | elsif cache_exists?(bundler_cache) && File.exists?(ruby_version_cache) && full_ruby_version != File.read(ruby_version_cache).chomp
680 | puts "Ruby version change detected. Clearing bundler cache."
681 | puts "Old: #{File.read(ruby_version_cache).chomp}"
682 | puts "New: #{full_ruby_version}"
683 | purge_bundler_cache
684 | end
685 |
686 | # fix git gemspec bug from Bundler 1.3.0+ upgrade
687 | if File.exists?(bundler_cache) && !File.exists?(bundler_version_cache) && !run("find vendor/bundle/*/*/bundler/gems/*/ -name *.gemspec").include?("No such file or directory")
688 | puts "Old bundler cache detected. Clearing bundler cache."
689 | purge_bundler_cache
690 | end
691 |
692 | # fix for https://github.com/heroku/heroku-buildpack-ruby/issues/86
693 | if (!File.exists?(rubygems_version_cache) ||
694 | (old_rubygems_version == "2.0.0" && old_rubygems_version != rubygems_version)) &&
695 | File.exists?(ruby_version_cache) && File.read(ruby_version_cache).chomp.include?("ruby 2.0.0p0")
696 | puts "Updating to rubygems #{rubygems_version}. Clearing bundler cache."
697 | purge_bundler_cache
698 | end
699 |
700 | FileUtils.mkdir_p(heroku_metadata)
701 | File.open(ruby_version_cache, 'w') do |file|
702 | file.puts full_ruby_version
703 | end
704 | File.open(buildpack_version_cache, 'w') do |file|
705 | file.puts BUILDPACK_VERSION
706 | end
707 | File.open(bundler_version_cache, 'w') do |file|
708 | file.puts BUNDLER_VERSION
709 | end
710 | File.open(rubygems_version_cache, 'w') do |file|
711 | file.puts rubygems_version
712 | end
713 | cache_store heroku_metadata
714 | end
715 |
716 | def purge_bundler_cache
717 | FileUtils.rm_rf(bundler_cache)
718 | cache_clear bundler_cache
719 | # need to reinstall language pack gems
720 | install_language_pack_gems
721 | end
722 | end
723 |
--------------------------------------------------------------------------------