heroku_hatchet 5.0.3 → 7.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +21 -1
- data/.gitignore +2 -0
- data/CHANGELOG.md +36 -1
- data/README.md +774 -174
- data/bin/hatchet +13 -6
- data/etc/ci_setup.rb +16 -12
- data/etc/setup_heroku.sh +0 -2
- data/hatchet.gemspec +4 -6
- data/hatchet.json +3 -2
- data/hatchet.lock +11 -9
- data/lib/hatchet.rb +1 -2
- data/lib/hatchet/api_rate_limit.rb +13 -24
- data/lib/hatchet/app.rb +150 -41
- data/lib/hatchet/config.rb +1 -1
- data/lib/hatchet/git_app.rb +29 -2
- data/lib/hatchet/reaper.rb +159 -56
- data/lib/hatchet/reaper/app_age.rb +49 -0
- data/lib/hatchet/reaper/reaper_throttle.rb +55 -0
- data/lib/hatchet/shell_throttle.rb +71 -0
- data/lib/hatchet/test_run.rb +16 -9
- data/lib/hatchet/version.rb +1 -1
- data/{test → repo_fixtures}/different-folder-for-checked-in-repos/default_ruby/Gemfile +0 -0
- data/spec/hatchet/allow_failure_git_spec.rb +55 -0
- data/spec/hatchet/app_spec.rb +226 -0
- data/spec/hatchet/ci_spec.rb +67 -0
- data/spec/hatchet/config_spec.rb +34 -0
- data/spec/hatchet/edit_repo_spec.rb +17 -0
- data/spec/hatchet/git_spec.rb +9 -0
- data/spec/hatchet/heroku_api_spec.rb +30 -0
- data/spec/hatchet/local_repo_spec.rb +26 -0
- data/spec/hatchet/lock_spec.rb +81 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/unit/reaper_spec.rb +169 -0
- data/spec/unit/shell_throttle.rb +28 -0
- metadata +43 -86
- data/test/fixtures/buildpacks/null-buildpack/bin/compile +0 -4
- data/test/fixtures/buildpacks/null-buildpack/bin/detect +0 -5
- data/test/fixtures/buildpacks/null-buildpack/bin/release +0 -3
- data/test/fixtures/buildpacks/null-buildpack/hatchet.json +0 -4
- data/test/fixtures/buildpacks/null-buildpack/readme.md +0 -41
- data/test/hatchet/allow_failure_git_test.rb +0 -16
- data/test/hatchet/app_test.rb +0 -96
- data/test/hatchet/ci_four_test.rb +0 -19
- data/test/hatchet/ci_test.rb +0 -11
- data/test/hatchet/ci_three_test.rb +0 -20
- data/test/hatchet/ci_too_test.rb +0 -19
- data/test/hatchet/config_test.rb +0 -51
- data/test/hatchet/edit_repo_test.rb +0 -20
- data/test/hatchet/git_test.rb +0 -16
- data/test/hatchet/heroku_api_test.rb +0 -30
- data/test/hatchet/labs_test.rb +0 -20
- data/test/hatchet/local_repo_test.rb +0 -26
- data/test/hatchet/lock_test.rb +0 -18
- data/test/hatchet/multi_cmd_runner_test.rb +0 -30
- data/test/test_helper.rb +0 -28
@@ -1,41 +0,0 @@
|
|
1
|
-
# Heroku Buildpack: Ø
|
2
|
-
|
3
|
-
Use Ø if you need Heroku to execute a binary.
|
4
|
-
|
5
|
-
## Usage
|
6
|
-
|
7
|
-
Create a directory for our Heroku app:
|
8
|
-
|
9
|
-
```bash
|
10
|
-
$ mkdir -p myapp/bin
|
11
|
-
$ cd myapp
|
12
|
-
```
|
13
|
-
|
14
|
-
Here is an example of an executable that will run on 64bit linux machine:
|
15
|
-
|
16
|
-
```bash
|
17
|
-
$ echo -e "#\!/usr/bin/env bash\n echo hello world" > ./bin/program
|
18
|
-
$ echo -e "program: bin/program" > Procfile
|
19
|
-
$ chmod +x ./bin/program
|
20
|
-
$ ./bin/program
|
21
|
-
hello world
|
22
|
-
```
|
23
|
-
|
24
|
-
Push the app to Heroku and run our executable:
|
25
|
-
|
26
|
-
```bash
|
27
|
-
$ git init; git add .; git commit -am 'init'
|
28
|
-
$ heroku create --buildpack http://github.com/ryandotsmith/null-buildpack.git
|
29
|
-
$ git push heroku master
|
30
|
-
$ heroku run program
|
31
|
-
Running `program` attached to terminal... up, run.8663
|
32
|
-
hello world
|
33
|
-
```
|
34
|
-
|
35
|
-
## Motivation
|
36
|
-
|
37
|
-
I wanted to run various executables (e.g. [log-shuttle](https://github.com/ryandotsmith/log-shuttle)) on Heroku without compiling them on Heroku. Thus, I compile programs on my linux 64 machine, or fetch the binary from the project, commit them to a repo and then run them on Heroku with the Ø buildpack.
|
38
|
-
|
39
|
-
## Issues
|
40
|
-
|
41
|
-
You will need to make sure that a 64bit linux machine can execute the binary.
|
@@ -1,16 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class AllowFailureGitTest < Minitest::Test
|
4
|
-
def test_allowed_failure
|
5
|
-
Hatchet::GitApp.new("no_lockfile", allow_failure: true).deploy do |app|
|
6
|
-
refute app.deployed?
|
7
|
-
assert_match "Gemfile.lock required", app.output
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def test_failure_with_no_flag
|
12
|
-
assert_raise(Hatchet::App::FailedDeploy) do
|
13
|
-
Hatchet::GitApp.new("no_lockfile").deploy
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
data/test/hatchet/app_test.rb
DELETED
@@ -1,96 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class AppTest < Minitest::Test
|
4
|
-
def test_app_with_default
|
5
|
-
app = Hatchet::App.new("default_ruby", buildpacks: [:default])
|
6
|
-
assert_match "https://github.com/heroku/heroku-buildpack-ruby", app.buildpacks.first
|
7
|
-
end
|
8
|
-
|
9
|
-
def test_create_app_with_stack
|
10
|
-
stack = "heroku-16"
|
11
|
-
app = Hatchet::App.new("default_ruby", stack: stack)
|
12
|
-
app.create_app
|
13
|
-
assert_equal stack, app.platform_api.app.info(app.name)["build_stack"]["name"]
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_before_deploy
|
17
|
-
@called = false
|
18
|
-
@dir = false
|
19
|
-
|
20
|
-
app = Hatchet::App.new("default_ruby")
|
21
|
-
def app.push_with_retry!; end # Don't actually deploy
|
22
|
-
|
23
|
-
app.before_deploy do
|
24
|
-
@called = true
|
25
|
-
@dir = Dir.pwd
|
26
|
-
end
|
27
|
-
|
28
|
-
app.deploy do
|
29
|
-
assert_equal true, @called
|
30
|
-
assert_equal Dir.pwd, @dir
|
31
|
-
end
|
32
|
-
|
33
|
-
refute_equal Dir.pwd, @dir
|
34
|
-
end
|
35
|
-
|
36
|
-
def test_auto_commits_code
|
37
|
-
string = "foo#{SecureRandom.hex}"
|
38
|
-
|
39
|
-
app = Hatchet::App.new("default_ruby")
|
40
|
-
def app.push_with_retry!; end # Don't actually deploy
|
41
|
-
|
42
|
-
app.before_deploy do |app|
|
43
|
-
assert_equal false, app.send(:needs_commit?)
|
44
|
-
`echo "#{string}" > Gemfile`
|
45
|
-
assert_equal true, app.send(:needs_commit?)
|
46
|
-
end
|
47
|
-
app.deploy do
|
48
|
-
assert_equal string, File.read("Gemfile").chomp
|
49
|
-
assert_equal false, app.send(:needs_commit?)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
|
54
|
-
def test_nested_in_directory
|
55
|
-
string = "foo#{SecureRandom.hex}"
|
56
|
-
|
57
|
-
app = Hatchet::App.new("default_ruby")
|
58
|
-
def app.push_with_retry!; end # Don't actually deploy
|
59
|
-
|
60
|
-
app.in_directory do
|
61
|
-
`echo "#{string}" > Gemfile`
|
62
|
-
dir = Dir.pwd
|
63
|
-
|
64
|
-
app.deploy do
|
65
|
-
assert_equal string, File.read("Gemfile").chomp
|
66
|
-
assert_equal Dir.pwd, dir
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def test_run
|
72
|
-
app = Hatchet::GitApp.new("default_ruby")
|
73
|
-
app.deploy do
|
74
|
-
assert_match(/ls: cannot access 'foo bar #baz': No such file or directory\s+Gemfile/, app.run("ls -a Gemfile 'foo bar #baz'"))
|
75
|
-
assert (0 != $?.exitstatus) # $? is from the app.run use of backticks
|
76
|
-
sleep(4) # Dynos don't exit right away and free dynos can't have more than one running at a time, wait before calling `run` again
|
77
|
-
|
78
|
-
app.run("ls erpderp", { :heroku => { "exit-code" => Hatchet::App::SkipDefaultOption } } )
|
79
|
-
assert (0 == $?.exitstatus) # $? is from the app.run use of backticks, but we asked the CLI not to return the program exit status by skipping the default "exit-code" option
|
80
|
-
sleep(4)
|
81
|
-
|
82
|
-
app.run("ls erpderp", { :heroku => { "no-tty" => nil } } )
|
83
|
-
assert (0 != $?.exitstatus) # $? is from the app.run use of backticks
|
84
|
-
sleep(4)
|
85
|
-
|
86
|
-
assert_match(/ohai world/, app.run('echo \$HELLO \$NAME', { raw: true, :heroku => { "env" => "HELLO=ohai;NAME=world" } } ))
|
87
|
-
sleep(4)
|
88
|
-
|
89
|
-
refute_match(/ohai world/, app.run('echo \$HELLO \$NAME', { raw: true, :heroku => { "env" => "" } } ))
|
90
|
-
sleep(4)
|
91
|
-
|
92
|
-
random_name = SecureRandom.hex
|
93
|
-
assert_match(/#{random_name}/, app.run("mkdir foo; touch foo/#{random_name}; ls foo/"))
|
94
|
-
end
|
95
|
-
end
|
96
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class CIFourTest < Minitest::Test
|
4
|
-
def test_error_with_bad_app
|
5
|
-
string = SecureRandom.hex
|
6
|
-
Hatchet::GitApp.new("default_ruby").run_ci do |test_run|
|
7
|
-
refute_match(string, test_run.output)
|
8
|
-
assert_match("Installing rake" , test_run.output)
|
9
|
-
|
10
|
-
run!(%Q{echo 'puts "#{string}"' >> Rakefile})
|
11
|
-
test_run.run_again
|
12
|
-
|
13
|
-
assert_match(string, test_run.output)
|
14
|
-
assert_match("Using rake" , test_run.output)
|
15
|
-
|
16
|
-
refute_match("Installing rake" , test_run.output)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
data/test/hatchet/ci_test.rb
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class CITest < Minitest::Test
|
4
|
-
def test_error_with_bad_app
|
5
|
-
error = assert_raise(Hatchet::FailedTestError) do
|
6
|
-
Hatchet::GitApp.new("rails5_ci_fails_no_database").run_ci {}
|
7
|
-
end
|
8
|
-
|
9
|
-
assert_match "PG::ConnectionBad: could not connect to server", error.message
|
10
|
-
end
|
11
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class CIThreeTest < Minitest::Test
|
4
|
-
def test_error_with_bad_app
|
5
|
-
@before_deploy_called = false
|
6
|
-
@before_deploy_dir_pwd = nil
|
7
|
-
before_deploy = -> {
|
8
|
-
@before_deploy_called = true
|
9
|
-
@before_deploy_dir_pwd = Dir.pwd
|
10
|
-
}
|
11
|
-
Hatchet::GitApp.new("rails5_ci_fails_no_database", allow_failure: true, before_deploy: before_deploy).run_ci do |test_run|
|
12
|
-
assert_equal :errored, test_run.status
|
13
|
-
assert_equal Dir.pwd, @before_deploy_dir_pwd
|
14
|
-
|
15
|
-
assert @before_deploy_called
|
16
|
-
end
|
17
|
-
|
18
|
-
refute_equal Dir.pwd, @before_deploy_dir_pwd
|
19
|
-
end
|
20
|
-
end
|
data/test/hatchet/ci_too_test.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
# Split out to be faster
|
4
|
-
class CITestToo < Minitest::Test
|
5
|
-
def test_ci_create_app_with_stack
|
6
|
-
app = Hatchet::GitApp.new("rails5_ruby_schema_format")
|
7
|
-
app.run_ci do |test_run|
|
8
|
-
assert_match "Ruby buildpack tests completed successfully", test_run.output
|
9
|
-
assert_equal :succeeded, test_run.status
|
10
|
-
refute_nil app.pipeline_id
|
11
|
-
|
12
|
-
api_rate_limit = app.api_rate_limit.call
|
13
|
-
couplings = api_rate_limit.pipeline_coupling.list_by_pipeline(app.pipeline_id)
|
14
|
-
coupled_app = api_rate_limit.app.info(couplings.first["app"]["id"])
|
15
|
-
assert_equal app.name, coupled_app["name"]
|
16
|
-
end
|
17
|
-
assert_nil app.pipeline_id
|
18
|
-
end
|
19
|
-
end
|
data/test/hatchet/config_test.rb
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class ConfigTest < Minitest::Test
|
4
|
-
|
5
|
-
def setup
|
6
|
-
@config = Hatchet::Config.new
|
7
|
-
end
|
8
|
-
|
9
|
-
def test_config_path_for_name
|
10
|
-
assert_equal 'test/fixtures/repos/rails3/rails3_mri_193', @config.path_for_name('rails3_mri_193')
|
11
|
-
end
|
12
|
-
|
13
|
-
def test_config_dirs
|
14
|
-
{
|
15
|
-
"test/fixtures/repos/bundler/no_lockfile" => "https://github.com/sharpstone/no_lockfile.git",
|
16
|
-
"test/fixtures/repos/default/default_ruby" => "https://github.com/sharpstone/default_ruby.git",
|
17
|
-
"test/fixtures/repos/rails2/rails2blog" => "https://github.com/sharpstone/rails2blog.git",
|
18
|
-
"test/fixtures/repos/rails3/rails3_mri_193" => "https://github.com/sharpstone/rails3_mri_193.git"
|
19
|
-
}.each do |key, value|
|
20
|
-
assert_include(key, value, @config.dirs)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def test_config_repos
|
25
|
-
{
|
26
|
-
"default_ruby" => "test/fixtures/repos/default/default_ruby",
|
27
|
-
"no_lockfile" => "test/fixtures/repos/bundler/no_lockfile",
|
28
|
-
"rails2blog" => "test/fixtures/repos/rails2/rails2blog",
|
29
|
-
"rails3_mri_193" => "test/fixtures/repos/rails3/rails3_mri_193"
|
30
|
-
}.each do |key, value|
|
31
|
-
assert_include(key, value, @config.repos)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def test_no_internal_config_raises_no_errors
|
36
|
-
# assert no_raise
|
37
|
-
@config.send :set_internal_config!, {}
|
38
|
-
assert_equal './repos', @config.repo_directory_path
|
39
|
-
end
|
40
|
-
|
41
|
-
def test_github_shortcuts
|
42
|
-
@config.send :init_config!, {"foo" => ["schneems/sextant"]}
|
43
|
-
assert_equal("https://github.com/schneems/sextant.git", @config.dirs["./repos/foo/sextant"])
|
44
|
-
end
|
45
|
-
private
|
46
|
-
|
47
|
-
def assert_include(key, value, actual)
|
48
|
-
assert_equal value, actual[key], "Expected #{actual.inspect} to include #{ {key => value } } but it did not"
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class EditRepoTest < Minitest::Test
|
4
|
-
def test_can_deploy_git_app
|
5
|
-
Hatchet::GitApp.new("default_ruby").in_directory do |app|
|
6
|
-
msg = `touch foo`
|
7
|
-
assert $?.success?, msg
|
8
|
-
|
9
|
-
msg = `git add .; git commit -m foo`
|
10
|
-
assert $?.success?, msg
|
11
|
-
|
12
|
-
assert_match "foo", `ls`
|
13
|
-
end
|
14
|
-
|
15
|
-
Hatchet::GitApp.new("default_ruby").in_directory do |app|
|
16
|
-
refute_match(/foo/, `ls`)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
data/test/hatchet/git_test.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class GitAppTest < Minitest::Test
|
4
|
-
def test_can_deploy_git_app
|
5
|
-
Hatchet::GitApp.new("rails5_ruby_schema_format").deploy do |app|
|
6
|
-
assert true
|
7
|
-
assert_match '2.4.1', app.run("ruby -v")
|
8
|
-
|
9
|
-
app.run("bash") do |cmd|
|
10
|
-
# cmd.run("cd public/assets")
|
11
|
-
cmd.run("ls public/") {|r| assert_match("assets", r) }
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class HerokuApiTest < Minitest::Test
|
4
|
-
|
5
|
-
def test_config_vars
|
6
|
-
runner = Hatchet::Runner.new("no_lockfile").setup!
|
7
|
-
expected = {}
|
8
|
-
actual = runner.get_config
|
9
|
-
actual.delete("BUILDPACK_URL")
|
10
|
-
assert_equal expected, actual
|
11
|
-
|
12
|
-
runner.set_config("foo" => "bar")
|
13
|
-
expected = {"foo" => "bar"}
|
14
|
-
actual = runner.get_config
|
15
|
-
actual.delete("BUILDPACK_URL")
|
16
|
-
assert_equal expected, actual
|
17
|
-
ensure
|
18
|
-
runner.teardown! if runner
|
19
|
-
end
|
20
|
-
|
21
|
-
|
22
|
-
def test_config_vars_in_init
|
23
|
-
runner = Hatchet::Runner.new("no_lockfile", config: { foo: "bar"}).setup!
|
24
|
-
actual = runner.get_config
|
25
|
-
expected = {"foo" => "bar"}
|
26
|
-
assert_equal expected, actual
|
27
|
-
ensure
|
28
|
-
runner.teardown! if runner
|
29
|
-
end
|
30
|
-
end
|
data/test/hatchet/labs_test.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class LabsTest < Minitest::Test
|
4
|
-
def setup
|
5
|
-
@buildpack_path = File.expand_path 'test/fixtures/buildpacks/heroku-buildpack-ruby'
|
6
|
-
end
|
7
|
-
|
8
|
-
def test_can_deploy_git_app
|
9
|
-
# You can't enable preboot on a free app.
|
10
|
-
# BUT you can't make an app hobby until you've deploy once
|
11
|
-
# Catch-22
|
12
|
-
# lab_name = "preboot"
|
13
|
-
# app = Hatchet::App.new("rails3_mri_193", labs: lab_name, buildpack: @buildpack_path)
|
14
|
-
# app.setup!
|
15
|
-
# assert(app.lab_is_installed?(lab_name), "Expected #{app.get_labs.inspect} to include {'name' => '#{lab_name}' } but it did not")
|
16
|
-
assert(true)
|
17
|
-
# ensure
|
18
|
-
# app.teardown! if app
|
19
|
-
end
|
20
|
-
end
|
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class LocalRepoTest < Minitest::Test
|
4
|
-
def test_in_directory_fork
|
5
|
-
env_name = SecureRandom.hex
|
6
|
-
ENV[env_name] = env_name
|
7
|
-
|
8
|
-
Hatchet::App.new("default_ruby").in_directory_fork do
|
9
|
-
ENV.delete(env_name)
|
10
|
-
assert_nil ENV[env_name]
|
11
|
-
end
|
12
|
-
|
13
|
-
assert_equal env_name, ENV[env_name]
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_repos_checked_into_git
|
17
|
-
app = Hatchet::App.new("test/different-folder-for-checked-in-repos/default_ruby")
|
18
|
-
app.in_directory do
|
19
|
-
assert_equal false, Dir.exist?(".git")
|
20
|
-
app.setup!
|
21
|
-
assert_equal true, Dir.exist?(".git")
|
22
|
-
end
|
23
|
-
ensure
|
24
|
-
app.teardown! if app
|
25
|
-
end
|
26
|
-
end
|
data/test/hatchet/lock_test.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class LockTest < Minitest::Test
|
4
|
-
def test_app_with_failure_can_be_locked_to_prior_commit
|
5
|
-
Hatchet::GitApp.new("lock_fail").deploy do |app|
|
6
|
-
assert app.deployed?
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
def test_app_with_failure_can_be_locked_to_master
|
11
|
-
puts `bundle exec hatchet lock`
|
12
|
-
|
13
|
-
lock = YAML.load_file("hatchet.lock")
|
14
|
-
name, branch = lock.select {|k,v| k.end_with?("lock_fail_master") }.first
|
15
|
-
assert_equal "test/fixtures/repos/lock/lock_fail_master", name
|
16
|
-
assert_equal "master", branch
|
17
|
-
end
|
18
|
-
end
|