rundoc 3.1.1 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -1
- data/README.md +18 -1
- data/lib/rundoc/cli_argument_parser.rb +1 -1
- data/lib/rundoc/code_command/background/process_spawn.rb +49 -4
- data/lib/rundoc/code_command/background/stdin_write.rb +36 -0
- data/lib/rundoc/code_command/background/stop.rb +1 -1
- data/lib/rundoc/code_command/background.rb +1 -0
- data/lib/rundoc/code_command/rundoc/require.rb +8 -3
- data/lib/rundoc/version.rb +1 -1
- data/test/integration/background_stdin_test.rb +57 -0
- data/test/integration/require_test.rb +34 -0
- data/test/rundoc/code_commands/background_test.rb +31 -3
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab49039bd205d3d147c1de91935b0deb1ffa095311eead39c750b426db1d7c87
|
4
|
+
data.tar.gz: f6a45b2b7a7b858ce09c8464ab891f9b3e7afe272de9d00768e5760a36f4533d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d946cf5081696a80ef34867e6559cb51f2191682e74ec21d44d524c41c11bdc05f73fe0f832afe9a89e5c85e6d6f588dfb85f857dbe141f2da278fc60df2527
|
7
|
+
data.tar.gz: 2c0fd644519f2cde9ca67ef6d56c9f1fa17727883149ebb0a437a9e163b70c41b478f1be39f50560fd7c7bf59ba77dab305d4654c2737fdfdc350098f094a549
|
data/CHANGELOG.md
CHANGED
@@ -1,12 +1,35 @@
|
|
1
1
|
## HEAD
|
2
2
|
|
3
|
+
## 4.0.0
|
4
|
+
|
5
|
+
- Add: Rundoc command `background.stdin_write` to send a string to a backtround process' STDIN. This allows driving REPL interfaces (https://github.com/zombocom/rundoc/pull/79)
|
6
|
+
|
7
|
+
```
|
8
|
+
:::>- background.start("ruby #{script}",
|
9
|
+
name: "script",
|
10
|
+
wait: ">",
|
11
|
+
timeout: 15
|
12
|
+
)
|
13
|
+
:::-- background.stdin_write("hello", name: "script", wait: "hello")
|
14
|
+
:::-- background.stdin_write("exit", name: "script", wait: "exit")
|
15
|
+
:::>> background.stop(name: "script")
|
16
|
+
```
|
17
|
+
|
18
|
+
- Changed: Rundoc command `background.stop` now outputs the log contents on `:::>>`. Previously it output nothing (https://github.com/zombocom/rundoc/pull/79)
|
19
|
+
- Changed: Strings passed into `background.wait` and similar `wait:` arguments of other background rundoc commands were accidentally being converted into regex, now they are matched as string literals. If you need regexes, please open an issue, they could be supporte but require peg_parser support (https://github.com/zombocom/rundoc/pull/79)
|
20
|
+
|
21
|
+
## 3.1.2
|
22
|
+
|
23
|
+
- Fix: Using `rundoc.require` inside of a document that was `rundoc.require`-d now sources files from the correct relative document path (https://github.com/zombocom/rundoc/pull/84)
|
24
|
+
- Fix: `rundoc <file> --with-contents <dir>` now expands the path passed in to the `--with-contents` so relative paths can safely be used (https://github.com/zombocom/rundoc/pull/83)
|
25
|
+
|
3
26
|
## 3.1.1
|
4
27
|
|
5
28
|
- Fix: Include all code sections in the event of a failure, not just the last one (https://github.com/zombocom/rundoc/pull/71)
|
6
29
|
|
7
30
|
## 3.1.0
|
8
31
|
|
9
|
-
- Add: `--with-contents` flag that accepts a directory. The **contents** of the directory (and not the directory itself) will be copied into the working dir before execution. This is useful for debugging a single rundoc step. ()
|
32
|
+
- Add: `--with-contents` flag that accepts a directory. The **contents** of the directory (and not the directory itself) will be copied into the working dir before execution. This is useful for debugging a single rundoc step. (https://github.com/zombocom/rundoc/pull/83)
|
10
33
|
|
11
34
|
For example if `RUNDOC.md` features many smaller docs:
|
12
35
|
|
data/README.md
CHANGED
@@ -86,9 +86,10 @@ This will generate a project folder with your project in it, and a markdown `REA
|
|
86
86
|
- Boot background processes such as a local server
|
87
87
|
- [background.start](#background)
|
88
88
|
- [background.stop](#background)
|
89
|
+
- [background.stdin_write](#background)
|
90
|
+
- [background.wait](#background)
|
89
91
|
- [background.log.read](#background)
|
90
92
|
- [background.log.clear](#background)
|
91
|
-
- [background.wait](#background)
|
92
93
|
- Take screenshots
|
93
94
|
- [website.visit](#screenshots)
|
94
95
|
- [website.nav](#screenshots)
|
@@ -424,6 +425,21 @@ You can make the background process wait until it receives a certain string in t
|
|
424
425
|
:::>> background.start("rails server", name: "server", wait: "Listening on")
|
425
426
|
```
|
426
427
|
|
428
|
+
You can send strings to the STDIN of the process:
|
429
|
+
|
430
|
+
```
|
431
|
+
:::>> background.start("heroku run bash", name: "heroku_run", wait: "$")
|
432
|
+
:::-- background.stdin_write("ls", name: "heroku_run")
|
433
|
+
```
|
434
|
+
|
435
|
+
- Arguments
|
436
|
+
- contents: Positional. The string to write to stdin
|
437
|
+
- ending: A string to append to the end of the contents, defaults to a newline
|
438
|
+
you could set it to something like ";" or an empty newline ""
|
439
|
+
- name
|
440
|
+
- wait
|
441
|
+
- timeout
|
442
|
+
|
427
443
|
You can stop the process by referencing the name:
|
428
444
|
|
429
445
|
```
|
@@ -433,6 +449,7 @@ You can stop the process by referencing the name:
|
|
433
449
|
- Arguments
|
434
450
|
- name
|
435
451
|
|
452
|
+
|
436
453
|
You can also get the log contents:
|
437
454
|
|
438
455
|
```
|
@@ -111,7 +111,7 @@ module Rundoc
|
|
111
111
|
end
|
112
112
|
|
113
113
|
opts.on("--with-contents <dir>", "Copies contents of directory into the tmp working dir") do |v|
|
114
|
-
options[:with_contents_dir] = v
|
114
|
+
options[:with_contents_dir] = File.expand_path(v)
|
115
115
|
end
|
116
116
|
|
117
117
|
opts.on("--on-success-dir <dir>", "Success dir, relative to CWD. i.e. `<rundoc.md/dir>/#{CLI::DEFAULTS::ON_SUCCESS_DIR}/`.") do |v|
|
@@ -19,7 +19,6 @@ class Rundoc::CodeCommand::Background
|
|
19
19
|
# server.stop
|
20
20
|
# server.alive? # => false
|
21
21
|
#
|
22
|
-
#
|
23
22
|
# There are class level methods that can be used to "name" and record
|
24
23
|
# background processes. They can be used like this:
|
25
24
|
#
|
@@ -53,17 +52,31 @@ class Rundoc::CodeCommand::Background
|
|
53
52
|
@log = Pathname.new(log)
|
54
53
|
@log.dirname.mkpath
|
55
54
|
FileUtils.touch(@log)
|
55
|
+
@pipe_output, @pipe_input = IO.pipe
|
56
56
|
|
57
57
|
@command = "/usr/bin/env bash -c #{@command.shellescape} >> #{@log} #{out}"
|
58
58
|
@pid = nil
|
59
59
|
end
|
60
60
|
|
61
|
-
|
61
|
+
# Wait until a given string is found in the logs
|
62
|
+
#
|
63
|
+
# If the string is not found within the timeout, a Timeout::Error is raised
|
64
|
+
#
|
65
|
+
# Caution: The logs will not be cleared before waiting, so if the string is
|
66
|
+
# already present from a prior operation, then it will not wait at all.
|
67
|
+
#
|
68
|
+
# To ensure you're waiting for a brand new string, call `log.truncate(0)` first.
|
69
|
+
# which is accessible via `:::-- background.log.clear` in rundoc syntax.
|
70
|
+
#
|
71
|
+
# @param wait_value [String] the string to wait for
|
72
|
+
# @param timeout_value [Integer] the number of seconds to wait before raising a Timeout::Error
|
73
|
+
# @param file [Pathname] the file to read from, default is the log file
|
74
|
+
def wait(wait_value = nil, timeout_value = @timeout_value, file: @log)
|
62
75
|
call
|
63
76
|
return unless wait_value
|
64
77
|
|
65
78
|
Timeout.timeout(Integer(timeout_value)) do
|
66
|
-
until
|
79
|
+
until file.read.include?(wait_value)
|
67
80
|
sleep 0.01
|
68
81
|
end
|
69
82
|
end
|
@@ -78,10 +91,42 @@ class Rundoc::CodeCommand::Background
|
|
78
91
|
false
|
79
92
|
end
|
80
93
|
|
94
|
+
# Writes the contents along with an optional ending character to the STDIN of the backtround process
|
95
|
+
#
|
96
|
+
# @param contents [String] the contents to write to the STDIN of the background process
|
97
|
+
# @param ending [String] an optional string to append to the contents before writing default is a newline
|
98
|
+
# if you don't want an ending, pass `""`
|
99
|
+
# @param timeout [Integer] the number of seconds to wait before raising a Timeout::Error when writing to STDIN
|
100
|
+
# or waiting for a string to appear in the logs. That means that the process can wait for a maximum
|
101
|
+
# of `timeout * 2` seconds before raising a Timeout::Error.
|
102
|
+
# @param wait [String] the string to wait for in the logs before continuing. There's a race condition
|
103
|
+
# if the process is in the middle of printing out something to the logs, then the output
|
104
|
+
# you're waiting form might not come as a result of the stdin_write.
|
105
|
+
def stdin_write(contents, ending: $/, timeout: timeout_value, wait: nil)
|
106
|
+
log_file = File.new(@log)
|
107
|
+
before_write_bytes = log_file.size
|
108
|
+
begin
|
109
|
+
Timeout.timeout(Integer(timeout)) do
|
110
|
+
@pipe_input.print(contents + ending)
|
111
|
+
@pipe_input.flush
|
112
|
+
end
|
113
|
+
rescue Timeout::Error
|
114
|
+
raise "Timeout (#{timeout}s) waiting to write #{contents} to stdin. Log contents:\n'#{log.read}'"
|
115
|
+
end
|
116
|
+
|
117
|
+
# Ignore bytes written before we sent the STDIN message
|
118
|
+
log_file.seek(before_write_bytes)
|
119
|
+
wait(wait, timeout, file: log_file)
|
120
|
+
contents
|
121
|
+
end
|
122
|
+
|
81
123
|
def stop
|
82
124
|
return unless alive?
|
125
|
+
@pipe_input.close
|
83
126
|
Process.kill("TERM", -Process.getpgid(@pid))
|
84
127
|
Process.wait(@pid)
|
128
|
+
rescue Errno::ESRCH => e
|
129
|
+
puts "Error stopping process (command: #{command}): #{e}"
|
85
130
|
end
|
86
131
|
|
87
132
|
def check_alive!
|
@@ -89,7 +134,7 @@ class Rundoc::CodeCommand::Background
|
|
89
134
|
end
|
90
135
|
|
91
136
|
private def call
|
92
|
-
@pid ||= Process.spawn(@command, pgroup: true)
|
137
|
+
@pid ||= Process.spawn(@command, pgroup: true, in: @pipe_output)
|
93
138
|
end
|
94
139
|
end
|
95
140
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class Rundoc::CodeCommand::Background
|
2
|
+
# Will send contents to the background process via STDIN along with a newline
|
3
|
+
#
|
4
|
+
#
|
5
|
+
class StdinWrite < Rundoc::CodeCommand
|
6
|
+
def initialize(contents, name:, wait:, timeout: 5, ending: $/)
|
7
|
+
@contents = contents
|
8
|
+
@ending = ending
|
9
|
+
@spawn = Rundoc::CodeCommand::Background::ProcessSpawn.find(name)
|
10
|
+
@wait = wait
|
11
|
+
@timeout_value = Integer(timeout)
|
12
|
+
@contents_written = nil
|
13
|
+
end
|
14
|
+
|
15
|
+
# The command is rendered (`:::>-`) by the output of the `def call` method.
|
16
|
+
def to_md(env = {})
|
17
|
+
writecontents
|
18
|
+
end
|
19
|
+
|
20
|
+
# The contents produced by the command (`:::->`) are rendered by the `def to_md` method.
|
21
|
+
def call(env = {})
|
22
|
+
writecontents
|
23
|
+
@spawn.log.read
|
24
|
+
end
|
25
|
+
|
26
|
+
def writecontents
|
27
|
+
@contents_written ||= @spawn.stdin_write(
|
28
|
+
contents,
|
29
|
+
wait: @wait,
|
30
|
+
ending: @ending,
|
31
|
+
timeout: @timeout_value
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
Rundoc.register_code_command(:"background.stdin_write", Rundoc::CodeCommand::Background::StdinWrite)
|
@@ -14,12 +14,17 @@ class ::Rundoc::CodeCommand
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def call(env = {})
|
17
|
-
|
17
|
+
execution_context = env[:context]
|
18
|
+
document_path = @path.expand_path(execution_context.source_dir)
|
18
19
|
|
19
|
-
puts "rundoc.require: Start executing #{@path.to_s.inspect}"
|
20
20
|
output = Rundoc::Parser.new(
|
21
21
|
document_path.read,
|
22
|
-
context:
|
22
|
+
context: Rundoc::Context::Execution.new(
|
23
|
+
source_path: document_path,
|
24
|
+
output_dir: execution_context.output_dir,
|
25
|
+
screenshots_dirname: execution_context.screenshots_dir,
|
26
|
+
with_contents_dir: execution_context.with_contents_dir
|
27
|
+
)
|
23
28
|
).to_md
|
24
29
|
|
25
30
|
if render_result?
|
data/lib/rundoc/version.rb
CHANGED
@@ -0,0 +1,57 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class BackgroundStdinTest < Minitest::Test
|
4
|
+
def test_background_stdin_write
|
5
|
+
Dir.mktmpdir do |dir|
|
6
|
+
Dir.chdir(dir) do
|
7
|
+
dir = Pathname(dir)
|
8
|
+
script = dir.join("script.rb")
|
9
|
+
script.write <<~'EOF'
|
10
|
+
$stdout.sync = true
|
11
|
+
|
12
|
+
print "> "
|
13
|
+
while line = gets
|
14
|
+
puts line
|
15
|
+
if line.strip == "exit"
|
16
|
+
puts "Bye"
|
17
|
+
return
|
18
|
+
else
|
19
|
+
puts "You said: #{line}"
|
20
|
+
end
|
21
|
+
print "> "
|
22
|
+
end
|
23
|
+
EOF
|
24
|
+
|
25
|
+
source_path = dir.join("RUNDOC.md")
|
26
|
+
source_path.write <<~EOF
|
27
|
+
```
|
28
|
+
:::>- background.start("ruby #{script}",
|
29
|
+
name: "script",
|
30
|
+
wait: ">",
|
31
|
+
timeout: 15
|
32
|
+
)
|
33
|
+
:::-- background.stdin_write("hello", name: "script", wait: "hello")
|
34
|
+
:::-- background.stdin_write("exit", name: "script", wait: "exit")
|
35
|
+
:::>> background.stop(name: "script")
|
36
|
+
```
|
37
|
+
EOF
|
38
|
+
|
39
|
+
io = StringIO.new
|
40
|
+
Rundoc::CLI.new(
|
41
|
+
io: io,
|
42
|
+
source_path: source_path,
|
43
|
+
on_success_dir: dir.join(SUCCESS_DIRNAME)
|
44
|
+
).call
|
45
|
+
|
46
|
+
readme = dir.join(SUCCESS_DIRNAME).join("README.md").read
|
47
|
+
expected = <<~EOF
|
48
|
+
> hello
|
49
|
+
You said: hello
|
50
|
+
> exit
|
51
|
+
Bye
|
52
|
+
EOF
|
53
|
+
assert readme.include?(expected)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -60,4 +60,38 @@ class IntegrationRequireTest < Minitest::Test
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
63
|
+
|
64
|
+
def test_require_is_relative_to_current_file_not_source_file
|
65
|
+
Dir.mktmpdir do |dir|
|
66
|
+
Dir.chdir(dir) do
|
67
|
+
dir = Pathname(dir)
|
68
|
+
|
69
|
+
source_path = dir.join("RUNDOC.md")
|
70
|
+
source_path.write <<~EOF
|
71
|
+
```
|
72
|
+
:::>> rundoc.require "./src/a.md"
|
73
|
+
```
|
74
|
+
EOF
|
75
|
+
|
76
|
+
dir.join("src").tap(&:mkpath).join("a.md").write <<~EOF
|
77
|
+
```
|
78
|
+
:::-> rundoc.require "./b.md"
|
79
|
+
```
|
80
|
+
EOF
|
81
|
+
|
82
|
+
dir.join("src").join("b.md").write <<~EOF
|
83
|
+
```
|
84
|
+
:::-> print.text Hello World!
|
85
|
+
```
|
86
|
+
EOF
|
87
|
+
|
88
|
+
parsed = parse_contents(
|
89
|
+
source_path.read,
|
90
|
+
source_path: source_path
|
91
|
+
)
|
92
|
+
actual = parsed.to_md.gsub(Rundoc::CodeSection::AUTOGEN_WARNING, "")
|
93
|
+
assert_equal "Hello World!", actual.strip
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
63
97
|
end
|
@@ -1,11 +1,39 @@
|
|
1
1
|
require "test_helper"
|
2
2
|
|
3
3
|
class BackgroundTest < Minitest::Test
|
4
|
+
def test_stdin_with_cat_echo
|
5
|
+
Dir.mktmpdir do |dir|
|
6
|
+
Dir.chdir(dir) do
|
7
|
+
background_start = Rundoc::CodeCommand::Background::Start.new("cat",
|
8
|
+
name: "cat")
|
9
|
+
background_start.call
|
10
|
+
|
11
|
+
output = Rundoc::CodeCommand::Background::StdinWrite.new(
|
12
|
+
"hello there",
|
13
|
+
name: "cat",
|
14
|
+
wait: "hello"
|
15
|
+
).call
|
16
|
+
assert_equal("hello there" + $/, output)
|
17
|
+
|
18
|
+
Rundoc::CodeCommand::Background::Log::Clear.new(
|
19
|
+
name: "cat"
|
20
|
+
).call
|
21
|
+
|
22
|
+
output = Rundoc::CodeCommand::Background::StdinWrite.new(
|
23
|
+
"general kenobi",
|
24
|
+
name: "cat",
|
25
|
+
wait: "general"
|
26
|
+
).call
|
27
|
+
assert_equal("general kenobi" + $/, output)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
4
32
|
def test_process_spawn_gc
|
5
33
|
Dir.mktmpdir do |dir|
|
6
34
|
Dir.chdir(dir) do
|
7
35
|
file = "foo.txt"
|
8
|
-
|
36
|
+
run!("echo 'foo' >> #{file}")
|
9
37
|
|
10
38
|
background_start = Rundoc::CodeCommand::Background::Start.new("tail -f #{file}",
|
11
39
|
name: "tail2",
|
@@ -30,7 +58,7 @@ class BackgroundTest < Minitest::Test
|
|
30
58
|
Dir.mktmpdir do |dir|
|
31
59
|
Dir.chdir(dir) do
|
32
60
|
file = "foo.txt"
|
33
|
-
|
61
|
+
run!("echo 'foo' >> #{file}")
|
34
62
|
|
35
63
|
background_start = Rundoc::CodeCommand::Background::Start.new("tail -f #{file}",
|
36
64
|
name: "tail",
|
@@ -49,7 +77,7 @@ class BackgroundTest < Minitest::Test
|
|
49
77
|
output = log_clear.call
|
50
78
|
assert_equal("", output)
|
51
79
|
|
52
|
-
|
80
|
+
run!("echo 'bar' >> #{file}")
|
53
81
|
|
54
82
|
log_read = Rundoc::CodeCommand::Background::Log::Read.new(name: "tail")
|
55
83
|
output = log_read.call
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rundoc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.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: 2024-11-
|
11
|
+
date: 2024-11-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -206,6 +206,7 @@ files:
|
|
206
206
|
- lib/rundoc/code_command/background/log/read.rb
|
207
207
|
- lib/rundoc/code_command/background/process_spawn.rb
|
208
208
|
- lib/rundoc/code_command/background/start.rb
|
209
|
+
- lib/rundoc/code_command/background/stdin_write.rb
|
209
210
|
- lib/rundoc/code_command/background/stop.rb
|
210
211
|
- lib/rundoc/code_command/background/wait.rb
|
211
212
|
- lib/rundoc/code_command/bash.rb
|
@@ -261,6 +262,7 @@ files:
|
|
261
262
|
- test/fixtures/screenshot/rundoc.md
|
262
263
|
- test/fixtures/simple_git/rundoc.md
|
263
264
|
- test/integration/after_build_test.rb
|
265
|
+
- test/integration/background_stdin_test.rb
|
264
266
|
- test/integration/failure_test.rb
|
265
267
|
- test/integration/print_test.rb
|
266
268
|
- test/integration/require_test.rb
|