rundoc 2.0.1 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +10 -7
  3. data/CHANGELOG.md +15 -0
  4. data/CONTRIBUTING.md +25 -0
  5. data/README.md +35 -48
  6. data/Rakefile +5 -1
  7. data/bin/rundoc +4 -17
  8. data/lib/rundoc/cli.rb +208 -49
  9. data/lib/rundoc/cli_argument_parser.rb +140 -0
  10. data/lib/rundoc/code_command/bash.rb +8 -1
  11. data/lib/rundoc/code_command/rundoc/depend_on.rb +1 -24
  12. data/lib/rundoc/code_command/rundoc/require.rb +13 -7
  13. data/lib/rundoc/code_command/website/driver.rb +7 -5
  14. data/lib/rundoc/code_command/website/navigate.rb +1 -1
  15. data/lib/rundoc/code_command/website/screenshot.rb +7 -2
  16. data/lib/rundoc/code_command/website/visit.rb +4 -2
  17. data/lib/rundoc/code_section.rb +7 -7
  18. data/lib/rundoc/context/after_build.rb +14 -0
  19. data/lib/rundoc/context/execution.rb +22 -0
  20. data/lib/rundoc/parser.rb +8 -4
  21. data/lib/rundoc/version.rb +1 -1
  22. data/lib/rundoc.rb +13 -5
  23. data/rundoc.gemspec +1 -0
  24. data/test/fixtures/cnb/ruby/download.md +22 -0
  25. data/test/fixtures/cnb/ruby/image_structure.md +34 -0
  26. data/test/fixtures/cnb/ruby/intro.md +5 -0
  27. data/test/fixtures/cnb/ruby/multiple_langs.md +43 -0
  28. data/test/fixtures/cnb/ruby/rundoc.md +48 -0
  29. data/test/fixtures/cnb/ruby/what_is_pack_build.md +18 -0
  30. data/test/fixtures/cnb/shared/call_to_action.md +11 -0
  31. data/test/fixtures/cnb/shared/configure_builder.md +23 -0
  32. data/test/fixtures/cnb/shared/install_pack.md +14 -0
  33. data/test/fixtures/cnb/shared/pack_build.md +20 -0
  34. data/test/fixtures/cnb/shared/procfile.md +13 -0
  35. data/test/fixtures/cnb/shared/use_the_image.md +52 -0
  36. data/test/fixtures/cnb/shared/what_is_a_builder.md +18 -0
  37. data/test/fixtures/rails_4/rundoc.md +1 -1
  38. data/test/fixtures/rails_5/rundoc.md +1 -1
  39. data/test/fixtures/rails_7/rundoc.md +0 -1
  40. data/test/fixtures/rails_8/rundoc.md +481 -0
  41. data/test/fixtures/simple_git/rundoc.md +13 -0
  42. data/test/integration/after_build_test.rb +62 -0
  43. data/test/integration/print_test.rb +9 -9
  44. data/test/integration/require_test.rb +63 -0
  45. data/test/integration/website_test.rb +35 -0
  46. data/test/rundoc/cli_argument_parser_test.rb +118 -0
  47. data/test/rundoc/code_section_test.rb +40 -8
  48. data/test/rundoc/parser_test.rb +3 -3
  49. data/test/rundoc/peg_parser_test.rb +6 -6
  50. data/test/system/exe_cli_test.rb +231 -0
  51. data/test/test_helper.rb +74 -1
  52. metadata +41 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ffd4e661f146495e1f5a1c450ab4b053a0c625a980ef877c9d1b77b1061a9a0
4
- data.tar.gz: e2fa4c55862c6bb84a5aa5c9fd393942a47991ec8b7af6ecfe74a6bcfae91d3f
3
+ metadata.gz: 89f953193be8aea0831e65e251a1d0cc8a3796573fe5818a6eb4ae2ed0e489d4
4
+ data.tar.gz: 9ea2f3a42906fa0c79cbb39862ec5300b4688dcce15df8991cf0a99e2d7c60da
5
5
  SHA512:
6
- metadata.gz: 5ebdd506647124c812344520d0ed85a3c28cfc0443fba6d1436a3605c7b2156f99a9620f58bc56125303aeca3a33eef5bc84a17cc7031d9d86793c498203b492
7
- data.tar.gz: 1dec5d073813c7b3853ee29b9e6425a74e201eb14adb47c9eb662f17ccc5e1e35644d463e93e80d5bf315e235ff50d9533e9c67ebd43f1041770d6fd154d3f7c
6
+ metadata.gz: 4aba6794da79ce27d6a4072e0cda4c7e2525030f0bd7c7925aaca8708a3adbd2632a0bb32c2ea451bc5f58ebc4a674ab4012ec6ddaeae7114ffbcefbafb53b35
7
+ data.tar.gz: fef87625649965fdd3dd5817e55d73299e1ebf67cfb3f9895c45d2c26b410af566f43540490a7c4ec7198b31f86d9c14079701b25981f7689b99644775f8e55f
data/.gitignore CHANGED
@@ -1,14 +1,17 @@
1
- test/fixtures/*/project/
2
- test/fixtures/*/tmp/
3
- test/fixtures/*/myapp/
4
-
5
- test/fixtures/*/*/project/
6
- test/fixtures/*/*/tmp/
7
- test/fixtures/*/*/myapp/
1
+ test/fixtures/**/*/rundoc_output/
2
+ test/fixtures/**/*/rundoc_failure/
3
+ test/fixtures/**/*/project/
4
+ test/fixtures/**/*/tmp/
5
+ test/fixtures/**/*/myapp/
8
6
 
7
+ coverage/
9
8
 
10
9
  *.gem
11
10
 
12
11
  .DS_Store
13
12
  Gemfile.lock
14
13
  .env
14
+
15
+ # IDEs
16
+ .idea/
17
+ .vscode/
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  ## HEAD
2
2
 
3
+ ## 3.0.1
4
+
5
+ - Fix: Save in-progress work in the "failure" directory when the rundoc command is interrupted via a signal such as `SIGTERM` (https://github.com/zombocom/rundoc/pull/67)
6
+
7
+ ## 3.0.0
8
+
9
+ - Change: Default directories are now `rundoc_output` (instead of `project`) and `rundoc_failure` (instead of `tmp`).
10
+ - Change: Non-empty directories for success or failure paths will now halt execution unless `--force` is used
11
+ - Change: The CLI command `rundoc build --path <path>` is now `rundoc <path>`
12
+ - Change: `rundoc.depend_on` is removed in favor of `:::-- rundoc.require` (https://github.com/zombocom/rundoc/pull/58)
13
+ - Change: `Rundoc.project_root=` is removed, please use `Rundoc.after_build` instead (https://github.com/zombocom/rundoc/pull/58)
14
+ - Change: Location of screenshots is now consistent (https://github.com/zombocom/rundoc/pull/57)
15
+ - Fix: Bash commands now stream their outputs while they're running (https://github.com/zombocom/rundoc/pull/57)
16
+ - Change: Minimum selenium-webdriver is now 4.x. (https://github.com/zombocom/rundoc/pull/41)
17
+
3
18
  ## 2.0.1
4
19
 
5
20
  - Bugfix: Code blocks without a following newline are now being recognized and executed (https://github.com/zombocom/rundoc/pull/51)
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,25 @@
1
+ # Contributing
2
+
3
+ ## Run the tests
4
+
5
+ ```
6
+ $ bundle exec rake test
7
+ ```
8
+
9
+ ## Run one test
10
+
11
+ ```
12
+ $ bundle exec m <path/to/file.rb>
13
+ ```
14
+
15
+ ## Run the linter
16
+
17
+ ```
18
+ $ bundle exec standardrb
19
+ ```
20
+
21
+ ## Run the local cli
22
+
23
+ ```
24
+ $ bin/rundoc <path/to/rundoc.md>
25
+ ```
data/README.md CHANGED
@@ -4,29 +4,37 @@
4
4
 
5
5
  ## What
6
6
 
7
- This library allows you to "run" your docs and embed the code as well as results back into the documentation. Here's a quick example:
7
+ Turn your tutorials into tests and never let your docs be out of date again.
8
8
 
9
- Write documentation:
9
+ Start off by writing your tutorial in modified-markdown, then execute it with `rundoc`. If there's a problem with following the directions, then your tutorial will fail to build. When it succeeds, the real world output is embedded in the output markdown file. That means your tutorials will have the EXACT output that your readers will see.
10
10
 
11
- Install by running:
11
+ ## Quickstart
12
12
 
13
- ```
14
- :::>> $ gem install rails --no-document
15
- ```
13
+ Install the Ruby library:
16
14
 
17
- Now if you "run" this document you'll get this output:
15
+ $ gem install rundoc
18
16
 
19
- Install by running:
17
+ Make a rundoc file:
20
18
 
19
+ $ mkdir /tmp/rundoc-demo
20
+ $ cd /tmp/rundoc-demo
21
+ $ cat <<'EOF' > ./RUNDOC.md
21
22
  ```
22
- $ gem install rails --no-document
23
- Successfully installed rails-5.2.2
24
- 1 gem installed
23
+ :::>> $ echo Hello World
25
24
  ```
25
+ EOF
26
26
 
27
- The idea is that as your documentation "runs" it builds a working tutorial. It also acts as tests since if your docs become incorrect due to a typo or bit-rot then when you try to generate them, the process will fail.
27
+ Run it:
28
28
 
29
- Think of RunDOC as your ever-vigilant tech editor and writing partner.
29
+ $ rundoc --on-success-dir=rundoc_output ./RUNDOC.md
30
+
31
+ View the output
32
+
33
+ $ cat rundoc_output/README.md
34
+ ```
35
+ $ echo Hello World
36
+ Hello World
37
+ ```
30
38
 
31
39
  ## Install
32
40
 
@@ -44,15 +52,15 @@ gem 'rundoc'
44
52
 
45
53
  ## Use It
46
54
 
47
- Run the `rundoc build` command on any markdown file
55
+ Run the `rundoc` command on any rundoc-flavored markdown file:
48
56
 
49
57
  ```sh
50
- $ bin/rundoc build --path <test/fixtures/rails_7/rundoc.md>
58
+ $ rundoc <test/fixtures/rails_7/rundoc.md>
51
59
  ```
52
60
 
53
61
  > Note: This command will create and manipulate directories in the working directory of your source markdown file. Best practice is to have your source markdown file in its own empty directory.
54
62
 
55
- This will generate a project folder with your project in it, and a markdown README.md with the parsed output of the markdown docs, and a copy of the source.
63
+ This will generate a project folder with your project in it, and a markdown `README.md` with the parsed output of the markdown docs. See `rundoc --help` for more configuration options.
56
64
 
57
65
  ## Quick docs
58
66
 
@@ -87,7 +95,6 @@ This will generate a project folder with your project in it, and a markdown READ
87
95
  - Configure RunDOC
88
96
  - [rundoc.configure](#configure)
89
97
  - Import and compose documents
90
- - [rundoc.depend_on](#compose-multiple-rundoc-documents)
91
98
  - [rundoc.require](#compose-multiple-rundoc-documents)
92
99
 
93
100
  ## RunDOC Syntax
@@ -164,7 +171,6 @@ Different commands will do different things with this input. For example the `ru
164
171
  end
165
172
  ```
166
173
 
167
-
168
174
  And the `website.visit` command allows you to navigate and manipulate a webpage via a Capybara API:
169
175
 
170
176
  ```
@@ -220,14 +226,12 @@ Current Commands:
220
226
 
221
227
  Anything you pass to `$` will be run in a shell. If a shell command returns a non-zero exit status an error will be raised. If you expect a non-zero exit status use `fail.$` instead:
222
228
 
223
-
224
229
  ```
225
230
  :::>> fail.$ cat /dev/null/foo
226
231
  ```
227
232
 
228
233
  Even though this command returns a non zero exit status, the contents of the command will be written since we're stating that we don't care if the command fails. This would be the output:
229
234
 
230
-
231
235
  ```
232
236
  $ cat /dev/null/foo
233
237
  cat: /dev/null/foo: Not a directory
@@ -235,7 +239,6 @@ Even though this command returns a non zero exit status, the contents of the com
235
239
 
236
240
  Some commands may be custom, for example when running `cd` you likely want to change the working directory that your script is running in. To do this we need to run `Dir.chdir` instead of shelling out. So this works as you would expect:
237
241
 
238
-
239
242
  ```
240
243
  :::>> $ cd myapp/config
241
244
  :::>> $ cat database.yml
@@ -455,7 +458,6 @@ The result of the screenshot command will be to replace the code section with a
455
458
 
456
459
  Once you've visited a website you can further navigate using `website.nav` or `website.navigate`:
457
460
 
458
-
459
461
  ```
460
462
  :::>> website.visit(name: "localhost", url: "http://localhost:3000")
461
463
  :::>> website.navigate(name: "localhost")
@@ -487,26 +489,20 @@ The bucketeer addon on Heroku is supported out of the box. To specify project sp
487
489
 
488
490
  ## Compose multiple RunDOC documents
489
491
 
490
- If you're writing multiple tutorials that all are used together to build one larger project then you can declare dependencies inside of your RunDOC document.
491
-
492
- For example on day two (`day_two/rundoc.md`) of the tutorials you could:
492
+ You can also break up your document into smaller components using `rundoc.require`:
493
493
 
494
494
  ```
495
- :::-- rundoc.depend_on "../day_one/rundoc.md"
495
+ :::>> rundoc.require "../day_one/rundoc.md"
496
496
  ```
497
497
 
498
- Now when you build `day_two/rundoc.md` it will also run the steps in `day_one/rundoc.md` first. This way you don't have to copy and paste previous commands.
499
-
500
- You can also break up your document into smaller components:
498
+ This will prepend the code section with the generated contents of `rundoc.require`.
501
499
 
500
+ If you want to execute another tutorial as a pre-requisite but not embed the results you can use `:::--`:
502
501
 
503
502
  ```
504
- :::>> rundoc.require "../shared/rails_new.md"
503
+ :::-- rundoc.require "../day_one/rundoc.md"
505
504
  ```
506
505
 
507
- This will replace the code section with the generated contents of `rundoc.require`.
508
-
509
-
510
506
  ## Dotenv support
511
507
 
512
508
  If you need to specify project specific environment variables create a file called `.env` at the same directory as your `rundoc.md` and it will be imported. Add this file to your `.gitignore` so you don't accidentally share with the world
@@ -523,13 +519,12 @@ Note: Make sure you run this as a hidden command (with `-`).
523
519
 
524
520
  **After Build**
525
521
 
526
- This will eval any code you put under that line (in Ruby). If you want to run some code after you're done building your docs you could use `Rundoc.configure` block and call the `after_build` method like this:
527
-
522
+ This will eval any code you put under that line (in Ruby) when the build was successful but before the contents are finalized on disk. If you want to run some code after you're done building your docs you could use `Rundoc.configure` block and call the `after_build` method like this:
528
523
 
529
524
  ```
530
525
  :::-- rundoc.configure
531
526
  Rundoc.configure do |config|
532
- config.after_build do
527
+ config.after_build do |context|
533
528
  puts "you could push to GitHub here"
534
529
  puts "You could do anything here"
535
530
  puts "This code will run after the docs are done building"
@@ -537,19 +532,11 @@ This will eval any code you put under that line (in Ruby). If you want to run so
537
532
  end
538
533
  ```
539
534
 
535
+ The `context` object will have details about the structure of the output directory structure. The stable API is:
540
536
 
541
- **Project Root**
542
-
543
- By default your app builds in a `tmp` directory. If any failures occur the results will remain in `tmp`. On a successful build the contents are copied over to `project`. If you are generating a new rails project in your code `$ rails new myapp`. Then the finished directory would be in `project/myapp`. If you don't like the `./project` prefix you could tell RunDOC to output contents in `./myapp` instead.
544
-
545
- ```
546
- :::-- rundoc.configure
547
- Rundoc.configure do |config|
548
- config.project_root = "myapp"
549
- end
550
- ```
551
-
552
- This will also be the root directory that the `after_build` is executed in.
537
+ - `context.output_dir`: A [Pathname](https://rubyapi.org/3.3/o/pathname) containing the absolute path to the top level directory where all commands are were executed. If your script runs `rails new myapp` then this directory would contain another directory named `myapp`. Only modifications to this directory will be persisted to the final `--output-dir`.
538
+ - `context.screenshots_dir`: A [Pathname](https://rubyapi.org/3.3/o/pathname) containing the absolute path to the directory where screenshots were saved. It is guaranteed to be somewhere within the `context.output_dir`
539
+ - `context.output_markdown_path`: A [Pathname](https://rubyapi.org/3.3/o/pathname) containing the absolute path to the final markdown file. This is guaranteed to be in the `context.output_dir`
553
540
 
554
541
  **Filter Sensitive Info**
555
542
 
data/Rakefile CHANGED
@@ -10,6 +10,10 @@ task default: [:test]
10
10
  Rake::TestTask.new(:test) do |t|
11
11
  t.libs << "lib"
12
12
  t.libs << "test"
13
- t.pattern = "test/rundoc/**/*_test.rb"
13
+ t.pattern = [
14
+ "test/rundoc/**/*_test.rb",
15
+ "test/system/**/*_test.rb",
16
+ "test/integration/**/*_test.rb"
17
+ ]
14
18
  t.verbose = false
15
19
  end
data/bin/rundoc CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  $stdout.sync = true
4
+ $stderr.sync = true
4
5
 
5
6
  unless File.respond_to? :realpath
6
7
  class File #:nodoc:
@@ -13,22 +14,8 @@ end
13
14
  $: << File.expand_path(File.dirname(File.realpath(__FILE__)) + '/../lib')
14
15
 
15
16
  require 'rundoc'
16
- require 'thor'
17
17
 
18
- class RundocThorCLI < Thor
18
+ options = Rundoc::CLIArgumentParser.new(argv: ARGV).call.options
19
19
 
20
- def initialize(*args)
21
- super
22
- @path = options[:path]
23
- end
24
-
25
- default_task :help
26
-
27
- desc "build", "turns rundoc file into docs and a project"
28
- class_option :path, banner: "path/to/file.md", optional: true, default: 'rundoc.md'
29
- def build
30
- Rundoc::CLI.new.build(path: @path)
31
- end
32
- end
33
-
34
- RundocThorCLI.start(ARGV)
20
+ cli = Rundoc::CLI.new(**options)
21
+ cli.call
data/lib/rundoc/cli.rb CHANGED
@@ -1,82 +1,241 @@
1
1
  module Rundoc
2
2
  class CLI
3
- def build(path:)
4
- @path = Pathname.new(path).expand_path
5
- raise "#{@path} does not exist" unless File.exist?(@path)
6
- raise "Expecting #{@path} to be a rundoc markdown file" unless File.file?(@path)
7
- @working_dir = Pathname.new(File.expand_path("../", @path))
8
-
9
- dot_env_path = File.expand_path("../.env", @path)
10
- if File.exist?(dot_env_path)
3
+ module DEFAULTS
4
+ ON_FAILURE_DIR = # <path/to/rundoc.md/..> +
5
+ "rundoc_failure"
6
+ ON_SUCCESS_DIR = # <path/to/rundoc.md/..> +
7
+ "rundoc_output"
8
+ DOTENV_PATH = # <path/to/rundoc.md/..> +
9
+ ".env"
10
+ OUTPUT_FILENAME = "README.md"
11
+ SCREENSHOTS_DIR = "screenshots"
12
+ end
13
+
14
+ attr_reader :io, :on_success_dir, :on_failure_dir, :dotenv_path, :cli_cmd, :cli_args, :force
15
+ attr_reader :execution_context, :after_build_context
16
+
17
+ def initialize(
18
+ source_path:,
19
+ io: $stderr,
20
+ cli_cmd: $0,
21
+ cli_args: $*,
22
+ force: false,
23
+ dotenv_path: nil,
24
+ on_success_dir: nil,
25
+ on_failure_dir: nil,
26
+ output_filename: nil,
27
+ screenshots_dirname: nil
28
+ )
29
+ @io = io
30
+ @force = force
31
+ @cli_cmd = cli_cmd
32
+ @cli_args = cli_args
33
+
34
+ screenshots_dirname = check_relative_path(screenshots_dirname || DEFAULTS::SCREENSHOTS_DIR)
35
+ output_filename = check_relative_path(output_filename || DEFAULTS::OUTPUT_FILENAME)
36
+
37
+ @execution_context = Rundoc::Context::Execution.new(
38
+ output_dir: Dir.mktmpdir,
39
+ source_path: source_path,
40
+ screenshots_dirname: screenshots_dirname
41
+ )
42
+
43
+ @after_build_context = Context::AfterBuild.new(
44
+ output_dir: execution_context.output_dir,
45
+ screenshots_dir: execution_context.screenshots_dir,
46
+ output_markdown_path: @execution_context.output_dir.join(output_filename)
47
+ )
48
+
49
+ @dotenv_path = if dotenv_path
50
+ Pathname(dotenv_path)
51
+ else
52
+ @execution_context.source_dir.join(DEFAULTS::DOTENV_PATH)
53
+ end
54
+
55
+ @on_success_dir = if on_success_dir
56
+ Pathname(on_success_dir)
57
+ else
58
+ @execution_context.source_dir.join(DEFAULTS::ON_SUCCESS_DIR)
59
+ end.expand_path
60
+
61
+ @on_failure_dir = if on_failure_dir
62
+ Pathname(on_failure_dir)
63
+ else
64
+ @execution_context.source_dir.join(DEFAULTS::ON_FAILURE_DIR)
65
+ end.expand_path
66
+ end
67
+
68
+ def force?
69
+ force
70
+ end
71
+
72
+ # Ensures that the value passed in cannot escape the current directory
73
+ #
74
+ # Examples:
75
+ #
76
+ # - `/tmp/whatever` is invalid
77
+ # - `whatever/../..` is invalid
78
+ #
79
+ private def check_relative_path(path)
80
+ path = Pathname(path)
81
+ if path.absolute?
82
+ raise "Path must be relative but it is not: #{path}"
83
+ end
84
+
85
+ if path.each_filename.any? { |part| part == ".." }
86
+ raise "path cannot contain `..` but it does: #{path}"
87
+ end
88
+ path
89
+ end
90
+
91
+ def load_dotenv
92
+ if File.exist?(dotenv_path)
93
+ io.puts("Found .env file #{dotenv_path}, loading")
11
94
  require "dotenv"
12
- Dotenv.load(dot_env_path)
13
- ENV["AWS_ACCESS_KEY_ID"] ||= ENV["BUCKETEER_AWS_ACCESS_KEY_ID"]
95
+ Dotenv.load(dotenv_path)
14
96
  ENV["AWS_REGION"] ||= ENV["BUCKETEER_AWS_REGION"]
15
- ENV["AWS_SECRET_ACCESS_KEY"] ||= ENV["BUCKETEER_AWS_SECRET_ACCESS_KEY"]
16
97
  ENV["AWS_BUCKET_NAME"] ||= ENV["BUCKETEER_BUCKET_NAME"]
98
+ ENV["AWS_ACCESS_KEY_ID"] ||= ENV["BUCKETEER_AWS_ACCESS_KEY_ID"]
99
+ ENV["AWS_SECRET_ACCESS_KEY"] ||= ENV["BUCKETEER_AWS_SECRET_ACCESS_KEY"]
100
+ else
101
+ io.puts("## No .env file found #{dotenv_path}, skipping dotenv loading")
17
102
  end
103
+ end
18
104
 
19
- source_contents = File.read(@path)
20
- tmp_dir = @working_dir.join("tmp")
21
-
22
- FileUtils.remove_entry_secure(tmp_dir) if tmp_dir.exist?
23
- tmp_dir.mkdir
24
- banner = <<~HEREDOC
105
+ def prepend_cli_banner(contents)
106
+ <<~HEREDOC
25
107
  <!-- STOP
26
108
  This file was generated by a rundoc script, do not modify it.
27
109
 
28
110
  Instead modify the rundoc script and re-run it.
29
111
 
30
- Command: #{$0} #{$*.join(" ")}
112
+ Command: #{cli_cmd} #{cli_args.join(" ")}
31
113
  STOP -->
114
+ #{contents}
32
115
  HEREDOC
116
+ end
117
+
118
+ def check_directories_empty!
119
+ [on_success_dir, on_failure_dir].each do |dir|
120
+ dir.mkpath
121
+
122
+ next if Dir.empty?(dir)
33
123
 
34
- puts "== Running your docs"
35
- Dir.chdir(tmp_dir) do
36
- @output = Rundoc::Parser.new(source_contents, document_path: @path).to_md
37
- Rundoc.sanitize(@output)
38
- @output = "#{banner}\n#{@output}"
124
+ if force?
125
+ io.puts "## WARNING: #{dir} is not empty, it may be cleared due to running with the `--force` flag"
126
+ else
127
+ raise "## ABORTING: #{dir} is not empty, clear it or re-run with `--force` flag"
128
+ end
39
129
  end
130
+ end
40
131
 
41
- puts "== Done, run was successful"
42
- project_name = if Rundoc.project_root
43
- Rundoc.project_root.split("/").last
44
- else
45
- "project"
132
+ def call
133
+ io.puts "## Running your docs"
134
+ load_dotenv
135
+ check_directories_empty!
136
+
137
+ source_contents = execution_context.source_path.read
138
+ if on_failure_dir.exist? && !Dir.empty?(on_failure_dir)
139
+ io.puts "## earing on failure directory #{on_failure_dir}"
140
+ clean_dir(
141
+ dir: on_failure_dir,
142
+ description: "on failure directory"
143
+ )
46
144
  end
47
145
 
48
- project_dir = @working_dir.join(project_name)
146
+ io.puts "## Working dir is #{execution_context.output_dir}"
147
+ Dir.chdir(execution_context.output_dir) do
148
+ parser = Rundoc::Parser.new(
149
+ source_contents,
150
+ context: execution_context
151
+ )
152
+ output = begin
153
+ parser.to_md
154
+ rescue StandardError, SignalException => e
155
+ warn "Received exception: #{e.inspect}, cleaning up before re-raise"
156
+ on_fail
157
+ raise e
158
+ end
159
+
160
+ on_success(output)
161
+ end
162
+ ensure
163
+ # Stop any hanging background tasks
164
+ Rundoc::CodeCommand::Background::ProcessSpawn.tasks.each do |name, task|
165
+ next unless task.alive?
49
166
 
50
- FileUtils.remove_entry_secure(project_dir) if project_dir.exist?
167
+ io.puts "Warning background task is still running, cleaning up: name: #{name}"
168
+ task.stop
169
+ end
51
170
 
52
- cp_root = if Rundoc.project_root
53
- tmp_dir.join(Rundoc.project_root, ".")
54
- else
55
- tmp_dir.join(".")
171
+ if execution_context.output_dir.exist?
172
+ clean_dir(
173
+ dir: execution_context.output_dir,
174
+ description: "tmp working directory"
175
+ )
56
176
  end
177
+ end
178
+
179
+ private def clean_dir(dir:, description:)
180
+ io.puts "## Cleaning #{description}, #{dir}"
181
+
182
+ FileUtils.remove_entry_secure(dir)
183
+ end
57
184
 
58
- FileUtils.cp_r(cp_root, project_dir)
185
+ private def on_fail
186
+ io.puts "## Failed, debug contents are in #{on_failure_dir}"
187
+ on_failure_dir.mkpath
59
188
 
60
- FileUtils.remove_entry_secure(tmp_dir) if tmp_dir.exist?
189
+ move_dir_contents(
190
+ from: execution_context.output_dir,
191
+ to: on_failure_dir
192
+ )
193
+ end
61
194
 
62
- source_path = project_dir.join("README.md")
63
- puts "== Done, writing original source to #{source_path}"
64
- File.write(source_path, @output)
195
+ private def on_success(output)
196
+ io.puts "## Success! sanitizing output"
197
+ Rundoc.sanitize!(output)
198
+ output = prepend_cli_banner(output)
65
199
 
66
- puts "== Copying source"
67
- source_path = project_dir.join("copied-#{@path.to_s.split("/").last}")
68
- File.write(source_path, source_contents)
200
+ io.puts "## Writing RUNdoc output to #{after_build_context.output_markdown_path}"
201
+ after_build_context.output_markdown_path.write(output)
69
202
 
70
- Dir.chdir(project_dir) do
71
- Rundoc.run_after_build
203
+ begin
204
+ io.puts "## Running after build scripts "
205
+ Rundoc.run_after_build(after_build_context)
206
+ rescue => e
207
+ on_fail
208
+ raise e
72
209
  end
73
- ensure
74
- Rundoc::CodeCommand::Background::ProcessSpawn.tasks.each do |name, task|
75
- next unless task.alive?
76
210
 
77
- puts "Warning background task is still running, cleaning up: name: #{name}"
78
- task.stop
211
+ io.puts "## Saving to #{on_success_dir}"
212
+ if on_success_dir.exist?
213
+ clean_dir(
214
+ dir: on_success_dir,
215
+ description: "on success directory"
216
+ )
217
+ end
218
+ on_success_dir.mkpath
219
+
220
+ move_dir_contents(
221
+ from: execution_context.output_dir,
222
+ to: on_success_dir
223
+ )
224
+
225
+ io.puts "## Finished"
226
+ end
227
+
228
+ def move_dir_contents(from:, to:)
229
+ io.puts("## Moving contents from #{from} to #{to}")
230
+
231
+ Dir.glob(File.join(from, "{*,.*}")).each do |item|
232
+ next if item.end_with?(".", "..")
233
+
234
+ FileUtils.mv(item, to)
79
235
  end
80
236
  end
81
237
  end
82
238
  end
239
+
240
+ require_relative "context/execution"
241
+ require_relative "context/after_build"