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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d21f4ac48ad2eab762969c03ce386aeb097f99162d38716e4736de44c9bbac83
4
- data.tar.gz: 79f9be265a84d8474e3ed7d55d90781e9999790b6cf5e578943db89e0fec7ee9
3
+ metadata.gz: ab49039bd205d3d147c1de91935b0deb1ffa095311eead39c750b426db1d7c87
4
+ data.tar.gz: f6a45b2b7a7b858ce09c8464ab891f9b3e7afe272de9d00768e5760a36f4533d
5
5
  SHA512:
6
- metadata.gz: fcf456c89f09000ddeeaf7aa22654acf419f36294b9e88dec9735f9ec3fcab315085dd6ee97cbd63eb580078e009968219b5240a6fe7a6db4c022d1b878d97cb
7
- data.tar.gz: 8e2ee0bd6fa26b8b8906ec9b2267a9e98feedc40b54dc53ca457937a7199865c2c6b7663837a067802426321fc93740da3038289ea516139a573a1e5679c8724
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
- def wait(wait_value = nil, timeout_value = @timeout_value)
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 @log.read.match(wait_value)
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)
@@ -10,7 +10,7 @@ class Rundoc::CodeCommand::Background
10
10
 
11
11
  def call(env = {})
12
12
  @spawn.stop
13
- ""
13
+ @spawn.log.read
14
14
  end
15
15
  end
16
16
  end
@@ -7,3 +7,4 @@ require "rundoc/code_command/background/stop"
7
7
  require "rundoc/code_command/background/wait"
8
8
  require "rundoc/code_command/background/log/clear"
9
9
  require "rundoc/code_command/background/log/read"
10
+ require "rundoc/code_command/background/stdin_write"
@@ -14,12 +14,17 @@ class ::Rundoc::CodeCommand
14
14
  end
15
15
 
16
16
  def call(env = {})
17
- document_path = @path.expand_path(env[:context].source_dir)
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: env[: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?
@@ -1,3 +1,3 @@
1
1
  module Rundoc
2
- VERSION = "3.1.1"
2
+ VERSION = "4.0.0"
3
3
  end
@@ -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
- `echo 'foo' >> #{file}`
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
- `echo 'foo' >> #{file}`
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
- `echo 'bar' >> #{file}`
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: 3.1.1
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-22 00:00:00.000000000 Z
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