heroku_hatchet 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +2 -2
- data/CHANGELOG.md +14 -0
- data/lib/hatchet/app.rb +9 -1
- data/lib/hatchet/command_parser.rb +39 -0
- data/lib/hatchet/process_spawn.rb +33 -18
- data/lib/hatchet/repl_runner.rb +61 -0
- data/lib/hatchet/stream_exec.rb +25 -21
- data/lib/hatchet/tasks.rb +7 -0
- data/lib/hatchet/version.rb +1 -1
- data/lib/hatchet.rb +4 -0
- data/test/hatchet/allow_failure_anvil_test.rb +2 -2
- data/test/hatchet/command_parser_test.rb +54 -0
- data/test/hatchet/config_test.rb +1 -1
- data/test/hatchet/multi_cmd_runner_test.rb +28 -0
- data/test/hatchet/repl_runner_test.rb +20 -0
- data/test/hatchet/stream_exec_test.rb +12 -0
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f6fe23a7e5a091233694be4bc546232d45b13fe
|
4
|
+
data.tar.gz: 4c161fbcb5e7b5140b6025caa6a1b511635617c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22409adecd80f6761593dd2f4cfb68696722903294cc08e7b5fb45183661d12e7b16ed1f67dd51a726c57e6249a9105a331ab9f0e1e3541a90c381384a1454ec
|
7
|
+
data.tar.gz: 1c95c1a1777355e891760054b1b75ec3cf4c70963271bd492cfc1beb9496e6e811e3e288650041a0ec0583a5c358266ac17b3060cce4f292efa0f0fb7580855b
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -3,8 +3,8 @@ language: ruby
|
|
3
3
|
rvm:
|
4
4
|
- 2.0.0
|
5
5
|
before_script: bundle exec rake hatchet:setup_travis
|
6
|
-
script: bundle exec parallel_test test/hatchet -n
|
7
|
-
|
6
|
+
script: bundle exec parallel_test test/hatchet -n 9
|
7
|
+
after_script: bundle exec rake hatchet:teardown_travis
|
8
8
|
|
9
9
|
env:
|
10
10
|
global:
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## HEAD
|
2
|
+
|
3
|
+
|
4
|
+
## 0.2.0
|
5
|
+
|
6
|
+
- Add database method App#add_database
|
7
|
+
|
8
|
+
- Drastically improved reliability of `app.run` outputs.
|
9
|
+
|
10
|
+
- Add `rake hatchet:teardown_travis` task to put in `travis.yml`:
|
11
|
+
|
12
|
+
after_script: bundle exec rake hatchet:teardown_travis
|
13
|
+
|
14
|
+
|
1
15
|
## 0.1.1
|
2
16
|
|
3
17
|
- Allow auto retries of pushes by setting environment variable `HATCHET_RETRIES=3`
|
data/lib/hatchet/app.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module Hatchet
|
2
|
-
RETRIES = Integer(ENV['HATCHET_RETRIES'] || 1)
|
3
2
|
class App
|
4
3
|
attr_reader :name, :directory
|
5
4
|
|
@@ -28,6 +27,15 @@ module Hatchet
|
|
28
27
|
self.class.config
|
29
28
|
end
|
30
29
|
|
30
|
+
|
31
|
+
def add_database(db_name = 'heroku-postgresql:dev', match_val = "HEROKU_POSTGRESQL_[A-Z]+_URL")
|
32
|
+
Hatchet::RETRIES.times.retry do
|
33
|
+
heroku.post_addon(name, db_name)
|
34
|
+
_, value = heroku.get_config_vars(name).body.detect {|k, v| k.match(/#{match_val}/) }
|
35
|
+
heroku.put_config_vars(name, 'DATABASE_URL' => value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
31
39
|
# runs a command on heroku similar to `$ heroku run #foo`
|
32
40
|
# but programatically and with more control
|
33
41
|
def run(command, timeout = nil, &block)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# removes the commands from strings retrieved from stuff like `heroku run bash`
|
2
|
+
# since likely you care about the output, not the input
|
3
|
+
# this is especially useful for seeing if a given input command has finished running
|
4
|
+
# if we cannot find a valid input command and output command return the full unparsed string
|
5
|
+
module Hatchet
|
6
|
+
class CommandParser
|
7
|
+
attr_accessor :command
|
8
|
+
|
9
|
+
def initialize(command)
|
10
|
+
@command = command
|
11
|
+
@parsed_string = ""
|
12
|
+
@raw_string = ""
|
13
|
+
end
|
14
|
+
|
15
|
+
def regex
|
16
|
+
/#{Regexp.quote(command)}\r*\n+/
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse(string)
|
20
|
+
@raw_string = string
|
21
|
+
@parsed_string = string.split(regex).last
|
22
|
+
return self
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
@parsed_string || @raw_string
|
27
|
+
end
|
28
|
+
|
29
|
+
def missing_valid_output?
|
30
|
+
!has_valid_output?
|
31
|
+
end
|
32
|
+
|
33
|
+
def has_valid_output?
|
34
|
+
return false unless @raw_string.match(regex)
|
35
|
+
return false if @parsed_string.blank? || @parsed_string.strip.blank?
|
36
|
+
true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -3,18 +3,20 @@ module Hatchet
|
|
3
3
|
# spawns a process on Heroku, and keeps it open for writing
|
4
4
|
# like `heroku run bash`
|
5
5
|
class ProcessSpawn
|
6
|
-
attr_reader :command, :app, :timeout
|
7
|
-
|
6
|
+
attr_reader :command, :app, :timeout, :pid
|
8
7
|
TIMEOUT = 60 # seconds to bring up a heroku command like `heroku run bash`
|
9
8
|
|
10
9
|
def initialize(command, app, timeout = nil)
|
11
|
-
|
12
|
-
|
13
|
-
@
|
10
|
+
raise "need command" unless command.present?
|
11
|
+
raise "need app" unless app.present?
|
12
|
+
@command = "heroku run #{command} -a #{app.name}"
|
13
|
+
@ready_regex = "^run.*up.*#{command}"
|
14
|
+
@app = app
|
15
|
+
@timeout = timeout || TIMEOUT
|
14
16
|
end
|
15
17
|
|
16
18
|
def ready?
|
17
|
-
@ready ||= `heroku ps -a #{app.name}`.match(
|
19
|
+
@ready ||= `heroku ps -a #{app.name}`.match(/#{@ready_regex}/).present?
|
18
20
|
end
|
19
21
|
|
20
22
|
def not_ready?
|
@@ -28,25 +30,38 @@ module Hatchet
|
|
28
30
|
return true
|
29
31
|
end
|
30
32
|
|
33
|
+
# some REPL's don't sync standard out by default
|
34
|
+
# try to do it auto-magically
|
35
|
+
def repl_magic(repl)
|
36
|
+
case command
|
37
|
+
when /rails\s*console/, /\sirb\s/
|
38
|
+
# puts "magic for: '#{command}'"
|
39
|
+
repl.run("STDOUT.sync = true")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
31
43
|
# Open up PTY (pseudo terminal) to command like `heroku run bash`
|
32
44
|
# Wait for the dyno to deploy, then allow user to run arbitrary commands
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
heroku_command = "heroku run #{command} -a #{app.name}"
|
38
|
-
return `#{heroku_command}` if block.blank? # one off command, no block given
|
39
|
-
|
40
|
-
output, input, pid = PTY.spawn(heroku_command)
|
41
|
-
stream = StreamExec.new(input, output)
|
45
|
+
def spawn_repl
|
46
|
+
output, input, pid = PTY.spawn(command)
|
47
|
+
stream = StreamExec.new(output, input, pid)
|
48
|
+
repl = ReplRunner.new(stream)
|
42
49
|
stream.timeout("waiting for spawn", timeout) do
|
43
50
|
wait_for_spawn!
|
44
51
|
end
|
45
52
|
raise "Could not run: '#{command}', command took longer than #{timeout} seconds" unless self.ready?
|
46
|
-
|
53
|
+
|
54
|
+
repl_magic(repl)
|
55
|
+
repl.wait_for_boot(5) # important to get rid of startup info i.e. "booting rails console ..."
|
56
|
+
return repl
|
57
|
+
end
|
58
|
+
|
59
|
+
def run(&block)
|
60
|
+
return `#{command}` if block.blank? # one off command, no block given
|
61
|
+
|
62
|
+
yield repl = spawn_repl
|
47
63
|
ensure
|
48
|
-
|
49
|
-
Process.kill('TERM', pid) if pid.present?
|
64
|
+
repl.close if repl.present?
|
50
65
|
end
|
51
66
|
end
|
52
67
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# takes a StringExec class and attempts to parse commands out of it
|
2
|
+
module Hatchet
|
3
|
+
class ReplRunner
|
4
|
+
TIMEOUT = 1
|
5
|
+
RETRIES = 10
|
6
|
+
|
7
|
+
attr_accessor :repl
|
8
|
+
|
9
|
+
def initialize(repl, command_parser_klass = CommandParser)
|
10
|
+
@repl = repl
|
11
|
+
@command_parser_klass = command_parser_klass
|
12
|
+
end
|
13
|
+
|
14
|
+
def command_parser_klass
|
15
|
+
@command_parser_klass
|
16
|
+
end
|
17
|
+
|
18
|
+
# adds a newline cause thats what most repl-s need to run command
|
19
|
+
def write(cmd)
|
20
|
+
repl.write("#{cmd}\n")
|
21
|
+
end
|
22
|
+
|
23
|
+
def run(cmd, options = {})
|
24
|
+
timeout = options[:timeout] || TIMEOUT
|
25
|
+
retries = options[:retries] || RETRIES
|
26
|
+
|
27
|
+
write(cmd)
|
28
|
+
read(cmd, timeout, retries)
|
29
|
+
end
|
30
|
+
|
31
|
+
def wait_for_boot(timeout = 5)
|
32
|
+
repl.read(timeout)
|
33
|
+
end
|
34
|
+
|
35
|
+
def close
|
36
|
+
repl.close
|
37
|
+
end
|
38
|
+
|
39
|
+
# take in a command like "ls", and tries to find it in the output
|
40
|
+
# of the repl (StreamExec)
|
41
|
+
# Example
|
42
|
+
# output, input, pid = PTY.spawn('sh')
|
43
|
+
# stream = StreamExec.new(output, input, pid)
|
44
|
+
# repl_runner = ReplRunner.new(stream)
|
45
|
+
# repl_runner.write("ls\n")
|
46
|
+
# repl_runner.read
|
47
|
+
# # => "app\tconfig.ru Gemfile\t LICENSE.txt public\t script vendor\r\r\nbin\tdb\t Gemfile.lock log\t Rakefile\t test\r\r\nconfig\tdoc\t lib\t\t Procfile README.md tmp\r\r\n"
|
48
|
+
#
|
49
|
+
# if the command "ls" is not found, repl runner will continue to retry grabbing more output
|
50
|
+
def read(cmd, timeout = TIMEOUT, retries = RETRIES)
|
51
|
+
str = ""
|
52
|
+
command_parser = command_parser_klass.new(cmd)
|
53
|
+
retries.times.each do
|
54
|
+
next if command_parser.has_valid_output?
|
55
|
+
str << repl.read(timeout)
|
56
|
+
command_parser.parse(str)
|
57
|
+
end
|
58
|
+
return command_parser.to_s
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/hatchet/stream_exec.rb
CHANGED
@@ -1,19 +1,26 @@
|
|
1
1
|
require 'timeout'
|
2
|
+
|
2
3
|
module Hatchet
|
3
4
|
# runs arbitrary commands within a Heroku process
|
4
5
|
class StreamExec
|
5
|
-
attr_reader :input, :output
|
6
|
+
attr_reader :input, :output, :pid
|
6
7
|
TIMEOUT = 1 # seconds to run an arbitrary command on a heroku process like `$ls`
|
7
8
|
|
8
|
-
def initialize(input,
|
9
|
+
def initialize(output, input, pid)
|
9
10
|
@input = input
|
10
11
|
@output = output
|
12
|
+
@pid = pid
|
13
|
+
end
|
14
|
+
|
15
|
+
def write(cmd)
|
16
|
+
input.write(cmd)
|
17
|
+
rescue Errno::EIO => e
|
18
|
+
raise e, "#{e.message} | trying to write '#{cmd}'"
|
11
19
|
end
|
12
20
|
|
13
|
-
def run(cmd)
|
14
|
-
|
15
|
-
|
16
|
-
return read(cmd)
|
21
|
+
def run(cmd, timeout = TIMEOUT)
|
22
|
+
write(cmd)
|
23
|
+
return read(timeout)
|
17
24
|
end
|
18
25
|
|
19
26
|
def close
|
@@ -21,6 +28,8 @@ module Hatchet
|
|
21
28
|
input.close
|
22
29
|
output.close
|
23
30
|
end
|
31
|
+
ensure
|
32
|
+
Process.kill('TERM', pid) if pid.present?
|
24
33
|
end
|
25
34
|
|
26
35
|
# There be dragons - (You're playing with process deadlock)
|
@@ -29,21 +38,17 @@ module Hatchet
|
|
29
38
|
# First pull all contents from stdout (except we don't know how many there are)
|
30
39
|
# So we have to go until our process deadlocks, then we timeout and return the string
|
31
40
|
#
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
# # => "ls\r\r\napp\tconfig.ru Gemfile\t LICENSE.txt public\t script vendor\r\r\nbin\tdb\t Gemfile.lock log\t Rakefile\t test\r\r\nconfig\tdoc\t lib\t\t Procfile README.md tmp\r\r\n"
|
39
|
-
#
|
40
|
-
# Now we want to remove the original command ("ls\r\r\n") and return the remainder
|
41
|
-
def read(cmd, str = "")
|
42
|
-
timeout do
|
43
|
-
# this is guaranteed to timeout; output.each will not return
|
44
|
-
output.each { |line| str << line }
|
41
|
+
def read(timeout = TIMEOUT)
|
42
|
+
str = ""
|
43
|
+
while true
|
44
|
+
Timeout::timeout(timeout) do
|
45
|
+
str << output.readline
|
46
|
+
end
|
45
47
|
end
|
46
|
-
|
48
|
+
|
49
|
+
return str
|
50
|
+
rescue Timeout::Error, EOFError
|
51
|
+
return str
|
47
52
|
end
|
48
53
|
|
49
54
|
def timeout(msg = nil, val = TIMEOUT, &block)
|
@@ -55,4 +60,3 @@ module Hatchet
|
|
55
60
|
end
|
56
61
|
end
|
57
62
|
end
|
58
|
-
|
data/lib/hatchet/tasks.rb
CHANGED
data/lib/hatchet/version.rb
CHANGED
data/lib/hatchet.rb
CHANGED
@@ -10,6 +10,8 @@ require 'stringio'
|
|
10
10
|
|
11
11
|
|
12
12
|
module Hatchet
|
13
|
+
RETRIES = Integer(ENV['HATCHET_RETRIES'] || 1)
|
14
|
+
|
13
15
|
class App
|
14
16
|
end
|
15
17
|
end
|
@@ -18,6 +20,8 @@ require 'hatchet/version'
|
|
18
20
|
require 'hatchet/app'
|
19
21
|
require 'hatchet/anvil_app'
|
20
22
|
require 'hatchet/git_app'
|
23
|
+
require 'hatchet/command_parser'
|
21
24
|
require 'hatchet/stream_exec'
|
25
|
+
require 'hatchet/repl_runner'
|
22
26
|
require 'hatchet/process_spawn'
|
23
27
|
require 'hatchet/config'
|
@@ -20,13 +20,13 @@ class AllowFailureAnvilTest < Test::Unit::TestCase
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def test_retries
|
23
|
-
Hatchet
|
23
|
+
Hatchet.const_set(:RETRIES, 2)
|
24
24
|
assert_raise(Anvil::Builder::BuildError) do
|
25
25
|
app = Hatchet::AnvilApp.new("no_lockfile", buildpack: @buildpack_path)
|
26
26
|
app.expects(:push!).twice.raises(Anvil::Builder::BuildError)
|
27
27
|
app.deploy
|
28
28
|
end
|
29
29
|
ensure
|
30
|
-
Hatchet
|
30
|
+
Hatchet.const_set(:RETRIES, 1)
|
31
31
|
end
|
32
32
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class CommandParserTest < Test::Unit::TestCase
|
4
|
+
def test_removes_command_from_string
|
5
|
+
hash = {command: "1+1",
|
6
|
+
string: "1+1\r\r\n=> 2\r\r\n",
|
7
|
+
expect: "=> 2\r\r\n"
|
8
|
+
}
|
9
|
+
cp = Hatchet::CommandParser.new(hash[:command]).parse(hash[:string])
|
10
|
+
assert cp.has_valid_output?
|
11
|
+
assert_equal hash[:expect], cp.to_s
|
12
|
+
|
13
|
+
|
14
|
+
hash = {command: "ls",
|
15
|
+
string: "Running `bash` attached to terminal... up, run.8041\r\n\e[01;34m~\e[00m \e[01;32m$ \e[00mls\r\r\napp config\tdb Gemfile\t lib\tProcfile Rakefile script tmp\r\r\nbin config.ru\tdoc Gemfile.lock log\tpublic\t README.rdoc test vendor\r\r\n",
|
16
|
+
expect: "app config\tdb Gemfile\t lib\tProcfile Rakefile script tmp\r\r\nbin config.ru\tdoc Gemfile.lock log\tpublic\t README.rdoc test vendor\r\r\n"
|
17
|
+
}
|
18
|
+
cp = Hatchet::CommandParser.new(hash[:command]).parse(hash[:string])
|
19
|
+
assert cp.has_valid_output?
|
20
|
+
assert_equal hash[:expect], cp.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_returns_result_if_no_command_in_result
|
24
|
+
hash = {command: "ls",
|
25
|
+
string: "1+1\r\r\n=> 2\r\r\n",
|
26
|
+
expect: "1+1\r\r\n=> 2\r\r\n"
|
27
|
+
}
|
28
|
+
cp = Hatchet::CommandParser.new(hash[:command]).parse(hash[:string])
|
29
|
+
refute cp.has_valid_output?
|
30
|
+
assert_equal hash[:expect], cp.to_s
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_empty_string
|
34
|
+
hash = {command: "ls",
|
35
|
+
string: "",
|
36
|
+
expect: ""
|
37
|
+
}
|
38
|
+
cp = Hatchet::CommandParser.new(hash[:command]).parse(hash[:string])
|
39
|
+
refute cp.has_valid_output?
|
40
|
+
assert_equal hash[:expect], cp.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def test_partial_command_no_result
|
45
|
+
hash = {command: "1+1",
|
46
|
+
string: "1+1\r\r\n",
|
47
|
+
expect: "1+1\r\r\n"
|
48
|
+
}
|
49
|
+
cp = Hatchet::CommandParser.new(hash[:command]).parse(hash[:string])
|
50
|
+
assert_equal hash[:expect], cp.to_s
|
51
|
+
refute cp.has_valid_output?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
data/test/hatchet/config_test.rb
CHANGED
@@ -20,7 +20,7 @@ class ConfigTest < Test::Unit::TestCase
|
|
20
20
|
def test_config_repos
|
21
21
|
expected_repos = { "rails3_mri_193" => "test/fixtures/repos/rails3/rails3_mri_193",
|
22
22
|
"rails2blog" => "test/fixtures/repos/rails2/rails2blog",
|
23
|
-
"no_lockfile" =>"test/fixtures/repos/bundler/no_lockfile"}
|
23
|
+
"no_lockfile" => "test/fixtures/repos/bundler/no_lockfile"}
|
24
24
|
assert_equal expected_repos, @config.repos
|
25
25
|
end
|
26
26
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class MultiCmdRunnerTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
@buildpack_path = File.expand_path 'test/fixtures/buildpacks/heroku-buildpack-ruby'
|
6
|
+
end
|
7
|
+
|
8
|
+
# slow but needed, there are ghosts in the machine
|
9
|
+
# by running common command multiple times we can find them
|
10
|
+
def test_multi_repl_commands
|
11
|
+
Hatchet::AnvilApp.new("rails3_mri_193", buildpack: @buildpack_path).deploy do |app|
|
12
|
+
app.add_database
|
13
|
+
|
14
|
+
rand(3..7).times do
|
15
|
+
app.run("bash") do |bash|
|
16
|
+
assert_match /Gemfile/, bash.run("ls")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
rand(3..7).times do
|
21
|
+
app.run("rails console") do |console|
|
22
|
+
assert_match /foofoofoofoofoo/, console.run("'foo' * 5")
|
23
|
+
assert_match /hello world/, console.run("'hello ' + 'world'")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
class ReplRunnerTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_returns_full_output_if_command_not_found
|
7
|
+
command = "irb"
|
8
|
+
input = StringIO.new("bar")
|
9
|
+
bogus_output = StringIO.new("foo")
|
10
|
+
stream = Hatchet::StreamExec.new(bogus_output, input, 1)
|
11
|
+
repl = Hatchet::ReplRunner.new(stream)
|
12
|
+
repl.write("1+1")
|
13
|
+
assert_equal bogus_output.string, repl.read("1+1")
|
14
|
+
|
15
|
+
Hatchet::CommandParser.any_instance.expects(:parse).times(Hatchet::ReplRunner::RETRIES)
|
16
|
+
Hatchet::CommandParser.any_instance.stubs(:to_s)
|
17
|
+
repl.write("1+1")
|
18
|
+
repl.read("1+1")
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class StreamExecTest < Test::Unit::TestCase
|
4
|
+
def test_local_irb_stream
|
5
|
+
command = "irb"
|
6
|
+
output, input, pid = PTY.spawn(command)
|
7
|
+
stream = Hatchet::StreamExec.new(output, input, pid)
|
8
|
+
stream.run("STDOUT.sync = true\n")
|
9
|
+
assert_equal "1+1\r\n => 2 \r\n", stream.run("1+1\n")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: heroku_hatchet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Schneeman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-06-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: heroku-api
|
@@ -157,9 +157,11 @@ files:
|
|
157
157
|
- lib/hatchet.rb
|
158
158
|
- lib/hatchet/anvil_app.rb
|
159
159
|
- lib/hatchet/app.rb
|
160
|
+
- lib/hatchet/command_parser.rb
|
160
161
|
- lib/hatchet/config.rb
|
161
162
|
- lib/hatchet/git_app.rb
|
162
163
|
- lib/hatchet/process_spawn.rb
|
164
|
+
- lib/hatchet/repl_runner.rb
|
163
165
|
- lib/hatchet/stream_exec.rb
|
164
166
|
- lib/hatchet/tasks.rb
|
165
167
|
- lib/hatchet/version.rb
|
@@ -202,8 +204,12 @@ files:
|
|
202
204
|
- test/hatchet/allow_failure_anvil_test.rb
|
203
205
|
- test/hatchet/allow_failure_git_test.rb
|
204
206
|
- test/hatchet/anvil_test.rb
|
207
|
+
- test/hatchet/command_parser_test.rb
|
205
208
|
- test/hatchet/config_test.rb
|
206
209
|
- test/hatchet/git_test.rb
|
210
|
+
- test/hatchet/multi_cmd_runner_test.rb
|
211
|
+
- test/hatchet/repl_runner_test.rb
|
212
|
+
- test/hatchet/stream_exec_test.rb
|
207
213
|
- test/test_helper.rb
|
208
214
|
homepage: https://github.com/heroku/hatchet
|
209
215
|
licenses:
|
@@ -269,6 +275,10 @@ test_files:
|
|
269
275
|
- test/hatchet/allow_failure_anvil_test.rb
|
270
276
|
- test/hatchet/allow_failure_git_test.rb
|
271
277
|
- test/hatchet/anvil_test.rb
|
278
|
+
- test/hatchet/command_parser_test.rb
|
272
279
|
- test/hatchet/config_test.rb
|
273
280
|
- test/hatchet/git_test.rb
|
281
|
+
- test/hatchet/multi_cmd_runner_test.rb
|
282
|
+
- test/hatchet/repl_runner_test.rb
|
283
|
+
- test/hatchet/stream_exec_test.rb
|
274
284
|
- test/test_helper.rb
|