heroku_hatchet 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +14 -0
  3. data/CHANGELOG.md +4 -0
  4. data/README.md +2 -2
  5. data/Rakefile +6 -0
  6. data/hatchet.gemspec +3 -1
  7. data/hatchet.json +3 -2
  8. data/lib/hatchet.rb +2 -0
  9. data/lib/hatchet/anvil_app.rb +28 -18
  10. data/lib/hatchet/app.rb +19 -5
  11. data/lib/hatchet/git_app.rb +5 -2
  12. data/lib/hatchet/tasks.rb +17 -7
  13. data/lib/hatchet/version.rb +1 -1
  14. data/test/fixtures/buildpacks/heroku-buildpack-ruby/.gitignore +4 -0
  15. data/test/fixtures/buildpacks/heroku-buildpack-ruby/CHANGELOG.md +378 -0
  16. data/test/fixtures/buildpacks/heroku-buildpack-ruby/Gemfile +10 -0
  17. data/test/fixtures/buildpacks/heroku-buildpack-ruby/LICENSE +9 -0
  18. data/test/fixtures/buildpacks/heroku-buildpack-ruby/README.md +192 -0
  19. data/test/fixtures/buildpacks/heroku-buildpack-ruby/Rakefile +358 -0
  20. data/test/fixtures/buildpacks/heroku-buildpack-ruby/bin/compile +13 -0
  21. data/test/fixtures/buildpacks/heroku-buildpack-ruby/bin/detect +12 -0
  22. data/test/fixtures/buildpacks/heroku-buildpack-ruby/bin/release +9 -0
  23. data/test/fixtures/buildpacks/heroku-buildpack-ruby/hatchet.json +25 -0
  24. data/test/fixtures/buildpacks/heroku-buildpack-ruby/lib/language_pack.rb +27 -0
  25. data/test/fixtures/buildpacks/heroku-buildpack-ruby/lib/language_pack/base.rb +175 -0
  26. data/test/fixtures/buildpacks/heroku-buildpack-ruby/lib/language_pack/bundler_lockfile.rb +19 -0
  27. data/test/fixtures/buildpacks/heroku-buildpack-ruby/lib/language_pack/disable_deploys.rb +17 -0
  28. data/test/fixtures/buildpacks/heroku-buildpack-ruby/lib/language_pack/no_lockfile.rb +16 -0
  29. data/test/fixtures/buildpacks/heroku-buildpack-ruby/lib/language_pack/rack.rb +43 -0
  30. data/test/fixtures/buildpacks/heroku-buildpack-ruby/lib/language_pack/rails2.rb +91 -0
  31. data/test/fixtures/buildpacks/heroku-buildpack-ruby/lib/language_pack/rails3.rb +86 -0
  32. data/test/fixtures/buildpacks/heroku-buildpack-ruby/lib/language_pack/rails4.rb +66 -0
  33. data/test/fixtures/buildpacks/heroku-buildpack-ruby/lib/language_pack/ruby.rb +681 -0
  34. data/test/fixtures/buildpacks/heroku-buildpack-ruby/lib/language_pack/shell_helpers.rb +62 -0
  35. data/test/fixtures/buildpacks/heroku-buildpack-ruby/spec/bugs_spec.rb +11 -0
  36. data/test/fixtures/buildpacks/heroku-buildpack-ruby/spec/no_lockfile_spec.rb +10 -0
  37. data/test/fixtures/buildpacks/heroku-buildpack-ruby/spec/rails23_spec.rb +11 -0
  38. data/test/fixtures/buildpacks/heroku-buildpack-ruby/spec/rails3_spec.rb +22 -0
  39. data/test/fixtures/buildpacks/heroku-buildpack-ruby/spec/rails4_spec.rb +12 -0
  40. data/test/fixtures/buildpacks/heroku-buildpack-ruby/spec/rubies_spec.rb +38 -0
  41. data/test/fixtures/buildpacks/heroku-buildpack-ruby/spec/spec_helper.rb +35 -0
  42. data/test/fixtures/buildpacks/heroku-buildpack-ruby/support/s3/hmac +79 -0
  43. data/test/fixtures/buildpacks/heroku-buildpack-ruby/support/s3/s3 +223 -0
  44. data/test/fixtures/buildpacks/heroku-buildpack-ruby/vendor/syck_hack.rb +64 -0
  45. data/test/hatchet/allow_failure_anvil_test.rb +21 -0
  46. data/test/hatchet/allow_failure_git_test.rb +17 -0
  47. data/test/hatchet/anvil_test.rb +7 -7
  48. data/test/hatchet/config_test.rb +4 -2
  49. data/test/hatchet/git_test.rb +2 -2
  50. metadata +89 -8
@@ -0,0 +1,13 @@
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
@@ -0,0 +1,12 @@
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
@@ -0,0 +1,9 @@
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
+
@@ -0,0 +1,25 @@
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
+ "rails4": [
23
+ "sharpstone/rails4-manifest"
24
+ ]
25
+ }
@@ -0,0 +1,27 @@
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"
@@ -0,0 +1,175 @@
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
+
@@ -0,0 +1,19 @@
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
@@ -0,0 +1,17 @@
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
+
@@ -0,0 +1,16 @@
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
@@ -0,0 +1,43 @@
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
+
@@ -0,0 +1,91 @@
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
+