Skip to content

Commit ede8617

Browse files
committed
Add --force protection and EXE tests
1 parent 9c3e83b commit ede8617

File tree

6 files changed

+217
-7
lines changed

6 files changed

+217
-7
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## HEAD
22

3+
- Change: Non-empty directories for success or failure paths will now halt execution unless `--force` is used
34
- Change: The CLI command `rundoc build --path <path>` is now `rundoc <path>`
45
- Change: `rundoc.depend_on` is removed in favor of `:::-- rundoc.require` (https://github.com/zombocom/rundoc/pull/58)
56
- Change: `Rundoc.project_root=` is removed, please use `Rundoc.after_build` instead (https://github.com/zombocom/rundoc/pull/58)

CONTRIBUTING.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ $ bundle exec standardrb
2121
## Run the local cli
2222

2323
```
24-
$ bin/rundoc build --path <path/to/rundoc.md>
24+
$ bin/rundoc <path/to/rundoc.md>
2525
```

lib/rundoc/cli.rb

+30-6
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,24 @@ module DEFAULTS
1010
OUTPUT_FILENAME = "README.md"
1111
SCREENSHOTS_DIR = "screenshots"
1212
end
13-
attr_reader :io, :on_success_dir, :on_failure_dir, :dotenv_path, :cli_cmd, :cli_args
13+
14+
attr_reader :io, :on_success_dir, :on_failure_dir, :dotenv_path, :cli_cmd, :cli_args, :force
1415
attr_reader :execution_context, :after_build_context
1516

1617
def initialize(
1718
source_path:,
1819
io: $stderr,
1920
cli_cmd: $0,
2021
cli_args: $*,
22+
force: false,
2123
dotenv_path: nil,
2224
on_success_dir: nil,
2325
on_failure_dir: nil,
2426
output_filename: nil,
2527
screenshots_dir: nil
2628
)
2729
@io = io
30+
@force = force
2831
@cli_cmd = cli_cmd
2932
@cli_args = cli_args
3033

@@ -62,6 +65,10 @@ def initialize(
6265
end
6366
end
6467

68+
def force?
69+
self.force
70+
end
71+
6572
# Ensures that the value passed in cannot escape the current directory
6673
#
6774
# Examples:
@@ -108,13 +115,30 @@ def prepend_cli_banner(contents)
108115
HEREDOC
109116
end
110117

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)
123+
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
129+
end
130+
end
131+
111132
def call
112133
io.puts "## Running your docs"
113134
load_dotenv
135+
check_directories_empty!
114136

115137
source_contents = execution_context.source_path.read
116-
io.puts "## Clearing on failure directory #{on_failure_dir}"
117-
FileUtils.remove_entry_secure(on_failure_dir) if on_failure_dir.exist?
138+
if on_failure_dir.exist? && !Dir.empty?(on_failure_dir)
139+
io.puts "## Clearing on failure directory #{on_failure_dir}"
140+
FileUtils.remove_entry_secure(on_failure_dir)
141+
end
118142

119143
io.puts "## Working dir is #{execution_context.output_dir}"
120144
Dir.chdir(execution_context.output_dir) do
@@ -147,7 +171,7 @@ def call
147171
end
148172

149173
private def on_fail
150-
io.puts "## Rundoc failed, debug contents are in #{on_failure_dir}"
174+
io.puts "## Failed, debug contents are in #{on_failure_dir}"
151175
on_failure_dir.mkpath
152176

153177
copy_dir_contents(
@@ -157,7 +181,7 @@ def call
157181
end
158182

159183
private def on_success(output)
160-
io.puts "## Rundoc was successful, sanitizing output"
184+
io.puts "## Success! sanitizing output"
161185
Rundoc.sanitize!(output)
162186
output = prepend_cli_banner(output)
163187

@@ -184,7 +208,7 @@ def call
184208
to: on_success_dir
185209
)
186210

187-
io.puts "## RUNdoc is done"
211+
io.puts "## Finished"
188212
end
189213

190214
def copy_dir_contents(from:, to:)

lib/rundoc/cli_argument_parser.rb

+4
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ def to_cli
131131
opts.on("--dotenv-path <path>", "Path to a `.env` file to load environment variable, relative to current working directory.") do |v|
132132
options[:dotenv_path] = v
133133
end
134+
135+
opts.on("--force", "Delete contents of the success/failure dirs even if they're not empty") do |v|
136+
options[:force] = v
137+
end
134138
end
135139
end
136140
end

test/integration/after_build_test.rb

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ def test_modifies_directory_structure
2525
assert dir.join("project").join("lol").exist?
2626
assert dir.join("project").join("lol").join("rofl.txt").exist?
2727

28+
FileUtils.remove_entry_secure(dir.join("project"))
29+
2830
source_path.write <<~EOF
2931
```
3032
:::-- rundoc.configure

test/system/exe_cli_test.rb

+179
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,185 @@ def exe_path
55
root_dir.join("bin").join("rundoc")
66
end
77

8+
def test_help
9+
output = run!("#{exe_path} --help")
10+
assert output.include?("Usage:")
11+
end
12+
13+
def test_force_fail_dir_protection
14+
Dir.mktmpdir do |dir|
15+
key = SecureRandom.hex
16+
dir = Pathname(dir)
17+
rundoc = dir.join("RUNDOC.md")
18+
rundoc.write "Done"
19+
20+
not_empty = dir.join("tmp").tap(&:mkpath).join("not_empty.txt")
21+
not_empty.write("Not empty")
22+
23+
run!("#{exe_path} #{rundoc}", raise_on_nonzero_exit: false)
24+
assert !$?.success?
25+
26+
assert not_empty.exist?
27+
28+
run!("#{exe_path} #{rundoc} --force", raise_on_nonzero_exit: false)
29+
assert $?.success?
30+
assert !not_empty.exist?
31+
end
32+
end
33+
34+
def test_force_success_dir_protection
35+
Dir.mktmpdir do |dir|
36+
key = SecureRandom.hex
37+
dir = Pathname(dir)
38+
39+
rundoc = dir.join("RUNDOC.md")
40+
rundoc.write "Done"
41+
42+
not_empty = dir.join("project").tap(&:mkpath).join("not_empty.txt")
43+
not_empty.write("Not empty")
44+
45+
run!("#{exe_path} #{rundoc}", raise_on_nonzero_exit: false)
46+
assert !$?.success?
47+
48+
assert not_empty.exist?
49+
50+
run!("#{exe_path} #{rundoc} --force", raise_on_nonzero_exit: false)
51+
assert $?.success?
52+
assert !not_empty.exist?
53+
end
54+
end
55+
56+
def test_dotenv
57+
Dir.mktmpdir do |dir|
58+
key = SecureRandom.hex
59+
dir = Pathname(dir)
60+
dotenv = dir.join("another").join("directory").tap(&:mkpath).join(".env")
61+
62+
dotenv.write <<~EOF
63+
FLORP="#{key}"
64+
EOF
65+
66+
rundoc = dir.join("RUNDOC.md")
67+
rundoc.write <<~EOF
68+
```
69+
:::>> $ echo $FLORP
70+
```
71+
EOF
72+
73+
run!("#{exe_path} #{rundoc} --dotenv-path #{dotenv}")
74+
75+
readme = dir.join("project")
76+
.tap {|p| assert p.exist? }
77+
.join("README.md")
78+
.tap {|p| assert p.exist? }
79+
80+
actual = strip_autogen_warning(readme.read)
81+
expected = <<~EOF
82+
```
83+
$ echo $FLORP
84+
#{key}
85+
```
86+
EOF
87+
assert_equal expected.strip, actual.strip
88+
end
89+
end
90+
91+
def test_screenshots_dir
92+
Dir.mktmpdir do |dir|
93+
dir = Pathname(dir)
94+
rundoc = dir.join("RUNDOC.md")
95+
screenshots_dirname = "lol_screenshots"
96+
rundoc.write <<~EOF
97+
```
98+
:::>> website.visit(name: "example", url: "http://example.com")
99+
:::>> website.screenshot(name: "example")
100+
```
101+
EOF
102+
103+
run!("#{exe_path} #{rundoc} --screenshots-dir #{screenshots_dirname}")
104+
105+
screenshots_dir = dir.join("project")
106+
.tap {|p| assert p.exist? }
107+
.join(screenshots_dirname)
108+
.tap {|p| assert p.exist? }
109+
110+
111+
readme = dir.join("project").join("README.md").read
112+
actual = strip_autogen_warning(readme)
113+
114+
expected = "![Screenshot of http://example.com/](#{screenshots_dirname}/screenshot_1.png)"
115+
assert_equal expected, actual.strip
116+
end
117+
end
118+
119+
def test_output_filename
120+
Dir.mktmpdir do |dir|
121+
dir = Pathname(dir)
122+
rundoc = dir.join("RUNDOC.md")
123+
failure_dir = dir.join("lol")
124+
rundoc.write "Done"
125+
126+
assert !failure_dir.exist?
127+
128+
run!("#{exe_path} #{rundoc} --output-filename tutorial.md")
129+
130+
tutorial_md = dir.join("project")
131+
.tap {|p| assert p.exist? }
132+
.join("tutorial.md")
133+
.tap {|p| assert p.exist? }
134+
135+
actual = strip_autogen_warning(tutorial_md.read)
136+
expected = "Done"
137+
138+
assert_equal expected.strip, actual.strip
139+
end
140+
end
141+
142+
def test_on_failure_dir
143+
Dir.mktmpdir do |dir|
144+
dir = Pathname(dir)
145+
rundoc = dir.join("RUNDOC.md")
146+
failure_dir = dir.join("lol")
147+
rundoc.write <<~EOF
148+
```
149+
:::>> $ touch lol.txt
150+
:::>> $ echo "hello world" && exit 1
151+
```
152+
EOF
153+
154+
assert !failure_dir.exist?
155+
156+
run!("#{exe_path} #{rundoc} --on-failure-dir #{failure_dir}", raise_on_nonzero_exit: false)
157+
assert !$?.success?
158+
159+
assert failure_dir.exist?
160+
assert !Dir.exist?(dir.join("tmp"))
161+
assert failure_dir.join("lol.txt").exist?
162+
end
163+
end
164+
165+
def test_on_success_dir
166+
Dir.mktmpdir do |dir|
167+
dir = Pathname(dir)
168+
rundoc = dir.join("RUNDOC.md")
169+
success_dir = dir.join("lol")
170+
rundoc.write "Done"
171+
172+
assert !success_dir.exist?
173+
run!("#{exe_path} #{rundoc} --on-success-dir #{success_dir}")
174+
175+
assert success_dir.exist?
176+
assert !Dir.exist?(dir.join("project"))
177+
178+
readme = success_dir.join("README.md")
179+
actual = strip_autogen_warning(readme.read)
180+
expected = "Done"
181+
182+
assert_equal expected.strip, actual.strip
183+
end
184+
end
185+
186+
8187
def test_simple_file
9188
Dir.mktmpdir do |dir|
10189
dir = Pathname(dir)

0 commit comments

Comments
 (0)