heroku_hatchet 7.1.3 → 7.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,3 @@
1
1
  module Hatchet
2
- VERSION = "7.1.3"
2
+ VERSION = "7.3.3"
3
3
  end
@@ -5,7 +5,7 @@ describe "AllowFailureGitTest" do
5
5
  let(:release_fail_proc) {
6
6
  Proc.new do
7
7
  File.open("Procfile", "w+") do |f|
8
- f.write <<~EOM
8
+ f.write <<-EOM.strip_heredoc
9
9
  release: echo "failing on release" && exit 1
10
10
  EOM
11
11
  end
@@ -1,24 +1,40 @@
1
1
  require("spec_helper")
2
2
 
3
3
  describe "AppTest" do
4
- it "rate throttles `git push` " do
5
- app = Hatchet::GitApp.new("default_ruby")
6
- def app.git_push_heroku_yall
7
- @_git_push_heroku_yall_call_count ||= 0
8
- @_git_push_heroku_yall_call_count += 1
9
- if @_git_push_heroku_yall_call_count >= 2
10
- "Success"
11
- else
12
- raise Hatchet::App::FailedDeployError.new(self, "message", output: "Your account reached the API rate limit Please wait a few minutes before making new requests")
4
+ it "annotates rspec expectation failures" do
5
+ app = Hatchet::Runner.new("default_ruby")
6
+ error = nil
7
+ begin
8
+ app.annotate_failures do
9
+ expect(true).to eq(false)
13
10
  end
11
+ rescue RSpec::Expectations::ExpectationNotMetError => e
12
+ error = e
14
13
  end
15
14
 
16
- def app.sleep_called?; @sleep_called; end
15
+ expect(error.message).to include(app.name)
16
+ end
17
+
18
+ it "does not modify local files by mistake" do
19
+ Dir.mktmpdir do |dir_1|
20
+ app = Hatchet::Runner.new(dir_1)
21
+ Dir.mktmpdir do |dir_2|
22
+ Dir.chdir(dir_2) do
23
+ FileUtils.touch("foo.txt")
24
+
25
+ app.setup!
26
+ end
27
+
28
+ entries_array = Dir.entries(dir_2)
29
+ entries_array -= ["..", ".", "foo.txt"]
30
+ expect(entries_array).to be_empty
17
31
 
18
- def app.what_is_git_push_heroku_yall_call_count; @_git_push_heroku_yall_call_count; end
19
- app.push_without_retry!
20
32
 
21
- expect(app.what_is_git_push_heroku_yall_call_count).to be(2)
33
+ entries_array = Dir.entries(dir_1)
34
+ entries_array -= ["..", ".", "foo.txt"]
35
+ expect(entries_array).to be_empty
36
+ end
37
+ end
22
38
  end
23
39
 
24
40
  it "calls reaper if cannot create an app" do
@@ -80,22 +96,75 @@ describe "AppTest" do
80
96
  expect(app_update_info["maintenance"]).to be_truthy
81
97
  end
82
98
 
83
- it "before deploy" do
84
- @called = false
85
- @dir = false
86
- app = Hatchet::App.new("default_ruby")
87
- def app.push_with_retry!
88
- # do nothing
99
+ describe "before deploy" do
100
+ it "dir" do
101
+ @called = false
102
+ @dir = false
103
+ app = Hatchet::App.new("default_ruby")
104
+ def app.push_with_retry!
105
+ # do nothing
106
+ end
107
+ app.before_deploy do
108
+ @called = true
109
+ @dir = Dir.pwd
110
+ end
111
+ app.deploy do
112
+ expect(@called).to eq(true)
113
+ expect(@dir).to eq(Dir.pwd)
114
+ end
115
+ expect(@dir).to_not eq(Dir.pwd)
89
116
  end
90
- app.before_deploy do
91
- @called = true
92
- @dir = Dir.pwd
117
+
118
+ it "prepend" do
119
+ @value = ""
120
+ app = Hatchet::App.new("default_ruby")
121
+ def app.push_with_retry!; end
122
+ app.before_deploy do
123
+ @value << "there"
124
+ end
125
+
126
+ app.before_deploy(:prepend) do
127
+ @value << "hello "
128
+ end
129
+ app.deploy do
130
+ end
131
+
132
+ expect(@value).to eq("hello there")
93
133
  end
94
- app.deploy do
95
- expect(@called).to eq(true)
96
- expect(@dir).to eq(Dir.pwd)
134
+
135
+ it "append" do
136
+ @value = ""
137
+ app = Hatchet::App.new("default_ruby")
138
+ def app.push_with_retry!; end
139
+ app.before_deploy do
140
+ @value << "there"
141
+ end
142
+
143
+ app.before_deploy(:append) do
144
+ @value << " hello"
145
+ end
146
+ app.deploy do
147
+ end
148
+
149
+ expect(@value).to eq("there hello")
150
+ end
151
+
152
+ it "replace" do
153
+ @value = ""
154
+ app = Hatchet::App.new("default_ruby")
155
+ def app.push_with_retry!; end
156
+ app.before_deploy do
157
+ @value << "there"
158
+ end
159
+
160
+ app.before_deploy(:replace) do
161
+ @value << "hello"
162
+ end
163
+ app.deploy do
164
+ end
165
+
166
+ expect(@value).to eq("hello")
97
167
  end
98
- expect(@dir).to_not eq(Dir.pwd)
99
168
  end
100
169
 
101
170
  it "auto commits code" do
@@ -11,16 +11,26 @@ describe "LocalRepoTest" do
11
11
  expect(ENV[env_name]).to eq(env_name)
12
12
  end
13
13
 
14
+ it "in directory fork captures stdout even when there is an error" do
15
+ expect {
16
+ Hatchet::App.new("default_ruby").in_directory_fork do
17
+ puts "hello_there"
18
+ raise "error"
19
+ end
20
+ }.to raise_error(/hello_there/)
21
+ end
22
+
14
23
  it "repos checked into git" do
15
24
  begin
16
- app = Hatchet::App.new("repo_fixtures/different-folder-for-checked-in-repos/default_ruby")
17
- app.in_directory do
18
- expect(Dir.exist?(".git")).to eq(false)
19
- app.setup!
20
- expect(Dir.exist?(".git")).to eq(true)
25
+ fixture_dir = "repo_fixtures/different-folder-for-checked-in-repos/default_ruby"
26
+ app = Hatchet::App.new(fixture_dir)
27
+ def app.push_with_retry!; end
28
+
29
+ expect(Dir.exist?("#{fixture_dir}/.git")).to be_falsey
30
+
31
+ app.deploy do
32
+ expect(Dir.exist?(".git")).to be_truthy
21
33
  end
22
- ensure
23
- app.teardown! if app
24
34
  end
25
35
  end
26
36
  end
@@ -57,7 +57,7 @@ describe "isolated lock tests" do
57
57
  end
58
58
 
59
59
  dir.join("hatchet.lock").open("w+") do |f|
60
- f.puts <<~EOM
60
+ f.puts <<-EOM.strip_heredoc
61
61
  ---
62
62
  - - "./repos/foo/lock_fail_main_default_is_master"
63
63
  - main
@@ -14,7 +14,8 @@ RSpec.configure do |config|
14
14
  end
15
15
  end
16
16
 
17
- ENV['HATCHET_BUILDPACK_BRANCH'] = "master"
17
+ ENV['HATCHET_BUILDPACK_BASE'] = "https://github.com/heroku/heroku-buildpack-ruby.git"
18
+ ENV['HATCHET_BUILDPACK_BRANCH'] = "main"
18
19
 
19
20
  require 'parallel_tests/test/runtime_logger' if ENV['RECORD_RUNTIME']
20
21
 
@@ -0,0 +1,115 @@
1
+ require "spec_helper"
2
+
3
+ describe "HerokuRun" do
4
+ def fake_app
5
+ app = Object.new
6
+ def app.name; "fake_app"; end
7
+ app
8
+ end
9
+
10
+ describe "options" do
11
+ it "escapes by default" do
12
+ run_obj = Hatchet::HerokuRun.new("ruby -v", app: fake_app)
13
+ expect(run_obj.command).to eq("heroku run --app=fake_app --exit-code -- ruby\\ -v")
14
+ end
15
+
16
+ it "escapes by default" do
17
+ run_obj = Hatchet::HerokuRun.new("ruby -v", app: fake_app, heroku: { "exit-code" => Hatchet::App::SkipDefaultOption })
18
+ expect(run_obj.command).to eq("heroku run --app=fake_app -- ruby\\ -v")
19
+ end
20
+
21
+ it "allows setting switch values by default" do
22
+ run_obj = Hatchet::HerokuRun.new("ruby -v", app: fake_app, heroku: { "no-tty" => nil })
23
+ expect(run_obj.command).to eq("heroku run --app=fake_app --exit-code --no-tty -- ruby\\ -v")
24
+ end
25
+
26
+ it "can be used to pass env vars" do
27
+ run_obj = Hatchet::HerokuRun.new("ruby -v", app: fake_app, heroku: { "env" => "HELLO=ohai;NAME=world" })
28
+ expect(run_obj.command).to eq("heroku run --app=fake_app --exit-code --env=HELLO\\=ohai\\;NAME\\=world -- ruby\\ -v")
29
+ end
30
+
31
+
32
+ it "lets me use raw values" do
33
+ run_obj = Hatchet::HerokuRun.new("ruby -v", app: fake_app, raw: true )
34
+ expect(run_obj.command).to eq("heroku run --app=fake_app --exit-code -- ruby -v")
35
+ end
36
+ end
37
+
38
+ describe "retry on empty" do
39
+ before(:all) do
40
+ @app = Hatchet::Runner.new("default_ruby")
41
+ @app.setup!
42
+ end
43
+
44
+ after(:all) do
45
+ @app.teardown!
46
+ end
47
+
48
+ it "retries 3 times on empty result" do
49
+ stderr = StringIO.new
50
+ run_obj = Hatchet::HerokuRun.new("ruby -v", app: @app, stderr: stderr)
51
+
52
+ def run_obj.run_shell!
53
+ @output = ""
54
+ @status = Object.new
55
+ end
56
+
57
+ run_obj.call
58
+
59
+ expect(run_obj.instance_variable_get(:@empty_fail_count)).to eq(3)
60
+ expect(stderr.string).to include("retrying the command.")
61
+ end
62
+
63
+ it "retries 0 times on NON empty result" do
64
+ stderr = StringIO.new
65
+ run_obj = Hatchet::HerokuRun.new("ruby -v", app: @app, stderr: stderr)
66
+
67
+ def run_obj.run_shell!
68
+ @output = "not empty"
69
+ @status = Object.new
70
+ end
71
+
72
+ run_obj.call
73
+
74
+ expect(run_obj.instance_variable_get(:@empty_fail_count)).to eq(0)
75
+ expect(run_obj.output).to eq("not empty")
76
+ end
77
+
78
+ it "retries 0 times on empty result when disabled" do
79
+ stderr = StringIO.new
80
+ run_obj = Hatchet::HerokuRun.new("ruby -v", app: @app, stderr: stderr, retry_on_empty: false)
81
+
82
+ def run_obj.run_shell!
83
+ @output = ""
84
+ @status = Object.new
85
+ end
86
+
87
+ run_obj.call
88
+
89
+ expect(run_obj.instance_variable_get(:@empty_fail_count)).to eq(0)
90
+ expect(stderr.string).to_not include("retrying the command.")
91
+ end
92
+
93
+ it "retries 0 times on empty result when disabled via ENV var" do
94
+ begin
95
+ original_env = ENV["HATCHET_DISABLE_EMPTY_RUN_RETRY"]
96
+ ENV["HATCHET_DISABLE_EMPTY_RUN_RETRY"] = "1"
97
+ stderr = StringIO.new
98
+ run_obj = Hatchet::HerokuRun.new("ruby -v", app: @app, stderr: stderr)
99
+
100
+ def run_obj.run_shell!
101
+ @output = ""
102
+ @status = Object.new
103
+ end
104
+
105
+ run_obj.call
106
+
107
+ expect(run_obj.instance_variable_get(:@empty_fail_count)).to eq(0)
108
+ expect(stderr.string).to_not include("retrying the command.")
109
+ ensure
110
+ ENV["HATCHET_DISABLE_EMPTY_RUN_RETRY"] = original_env
111
+ end
112
+ end
113
+ end
114
+ end
115
+
@@ -0,0 +1,52 @@
1
+ require "spec_helper"
2
+
3
+ describe "Hatchet::Init" do
4
+ def fake_buildpack_dir
5
+ Dir.mktmpdir do |dir|
6
+ FileUtils.mkdir_p("#{dir}/bin")
7
+ yield dir
8
+ end
9
+ end
10
+ it "raises an error when not pointing at the right directory" do
11
+ Dir.mktmpdir do |dir|
12
+ expect {
13
+ Hatchet::InitProject.new(dir: dir)
14
+ }.to raise_error(/Must run in a directory with a buildpack/)
15
+ end
16
+ end
17
+
18
+ # write_target(target: ".circleci/config.yml", template: "circleci_template.erb")
19
+ # write_target(target: "Gemfile", template: "Gemfile.erb")
20
+ # write_target(target: "hatchet.json", contents: "{}")
21
+ # write_target(target: "hatchet.lock", contents: YAML.dump({}))
22
+ # write_target(target: "spec/spec_helper.rb", template: "spec_helper.erb")
23
+ # write_target(target: "spec/hatchet/buildpack_spec.rb", template: "buildpack_spec.erb")
24
+ # write_target(target: ".github/dependabot.yml", template: "dependabot.erb")
25
+
26
+ it "generates files" do
27
+ fake_buildpack_dir do |dir|
28
+ fake_stdout = StringIO.new
29
+ init = Hatchet::InitProject.new(dir: dir, io: fake_stdout)
30
+ init.call
31
+
32
+ circle_ci_file = Pathname.new(dir).join(".circleci/config.yml")
33
+ expect(circle_ci_file.read).to match("parallel_split_test")
34
+
35
+ %W{
36
+ .circleci/config.yml
37
+ Gemfile
38
+ hatchet.json
39
+ hatchet.lock
40
+ spec/spec_helper.rb
41
+ spec/hatchet/buildpack_spec.rb
42
+ .github/dependabot.yml
43
+ .github/workflows/check_changelog.yml
44
+ .gitignore
45
+ }.each do |path|
46
+ expect(Pathname.new(dir).join(path)).to exist
47
+ end
48
+
49
+ expect(fake_stdout.string).to match("Bundle complete")
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,94 @@
1
+ require "spec_helper"
2
+
3
+ describe "ShellThrottle" do
4
+ before(:each) do
5
+ throttle = PlatformAPI.rate_throttle
6
+ def throttle.sleep(value)
7
+ # No sleep, faster tests
8
+ end
9
+ end
10
+
11
+ after(:each) do
12
+ throttle = PlatformAPI.rate_throttle
13
+ def throttle.sleep(value)
14
+ super # Unstub
15
+ end
16
+ end
17
+
18
+ describe "class unit test" do
19
+ before(:all) do
20
+ @platform_api = Hatchet::Runner.new("default_ruby").platform_api
21
+ end
22
+
23
+ it "throttles when throw is called" do
24
+ @count = 0
25
+ Hatchet::ShellThrottle.new(platform_api: @platform_api).call do
26
+ @count += 1
27
+ if @count >= 2
28
+ # No throttle
29
+ else
30
+ throw(:throttle)
31
+ end
32
+ end
33
+ expect(@count).to eq(2)
34
+ end
35
+
36
+ it "does not throttle when throw is NOT called" do
37
+ @count = 0
38
+ Hatchet::ShellThrottle.new(platform_api: @platform_api).call do
39
+ @count += 1
40
+ end
41
+ expect(@count).to eq(1)
42
+ end
43
+ end
44
+
45
+ describe "git push throttle" do
46
+ it "rate throttles `git push` " do
47
+ app = Hatchet::GitApp.new("default_ruby")
48
+ def app.git_push_heroku_yall
49
+ @_git_push_heroku_yall_call_count ||= 0
50
+ @_git_push_heroku_yall_call_count += 1
51
+ if @_git_push_heroku_yall_call_count >= 2
52
+ "Success"
53
+ else
54
+ raise Hatchet::App::FailedDeployError.new(
55
+ self,
56
+ "message",
57
+ output: "Your account reached the API rate limit Please wait a few minutes before making new requests"
58
+ )
59
+ end
60
+ end
61
+
62
+ def app.sleep_called?; @sleep_called; end
63
+ def app.what_is_git_push_heroku_yall_call_count; @_git_push_heroku_yall_call_count; end
64
+
65
+ app.push_without_retry!
66
+
67
+ expect(app.what_is_git_push_heroku_yall_call_count).to be(2)
68
+ end
69
+
70
+ it "rate throttles `git push` with different output" do
71
+ app = Hatchet::GitApp.new("default_ruby")
72
+ def app.git_push_heroku_yall
73
+ @_git_push_heroku_yall_call_count ||= 0
74
+ @_git_push_heroku_yall_call_count += 1
75
+ if @_git_push_heroku_yall_call_count >= 2
76
+ "Success"
77
+ else
78
+ raise Hatchet::App::FailedDeployError.new(
79
+ self,
80
+ "message",
81
+ output: "RPC failed; HTTP 429 curl 22 The requested URL returned error: 429 Too Many Requests"
82
+ )
83
+ end
84
+ end
85
+
86
+ def app.sleep_called?; @sleep_called; end
87
+ def app.what_is_git_push_heroku_yall_call_count; @_git_push_heroku_yall_call_count; end
88
+
89
+ app.push_without_retry!
90
+
91
+ expect(app.what_is_git_push_heroku_yall_call_count).to be(2)
92
+ end
93
+ end
94
+ end