Skip to content

Commit 054740b

Browse files
committed
Quiet bin/dev shutdown on interrupt
1 parent 03111e6 commit 054740b

11 files changed

Lines changed: 81 additions & 26 deletions

File tree

react_on_rails/lib/generators/react_on_rails/rsc_setup.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def add_rsc_to_procfile
167167
⚠️ Procfile.dev not found. Skipping RSC bundle watcher addition.
168168
169169
You'll need to add the RSC bundle watcher to your process manager manually:
170-
rsc-bundle: RSC_BUNDLE_ONLY=yes bin/shakapacker --watch
170+
rsc-bundle: RSC_BUNDLE_ONLY=yes bin/shakapacker-watch --watch
171171
MSG
172172
return
173173
end
@@ -182,7 +182,7 @@ def add_rsc_to_procfile
182182
rsc_watcher_line = <<~PROCFILE
183183
184184
# React on Rails Pro - RSC bundle watcher
185-
rsc-bundle: RSC_BUNDLE_ONLY=yes bin/shakapacker --watch
185+
rsc-bundle: RSC_BUNDLE_ONLY=yes bin/shakapacker-watch --watch
186186
PROCFILE
187187

188188
append_to_file("Procfile.dev", rsc_watcher_line)

react_on_rails/lib/generators/react_on_rails/templates/base/base/Procfile.dev

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66
# SHAKAPACKER_DEV_SERVER_PORT=3036
77
rails: bundle exec rails s -p ${PORT:-3000}
88
dev-server: bin/shakapacker-dev-server
9-
server-bundle: SERVER_BUNDLE_ONLY=yes bin/shakapacker --watch
9+
server-bundle: SERVER_BUNDLE_ONLY=yes bin/shakapacker-watch --watch
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
web: bin/rails server -p ${PORT:-3000}
2-
js: bin/shakapacker --watch
2+
js: bin/shakapacker-watch --watch
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env sh
2+
set -eu
3+
4+
child_pid=""
5+
6+
cleanup() {
7+
if [ -n "${child_pid}" ] && kill -0 "$child_pid" 2>/dev/null; then
8+
kill -TERM "$child_pid" 2>/dev/null || true
9+
wait "$child_pid" 2>/dev/null || true
10+
fi
11+
12+
exit 0
13+
}
14+
15+
trap cleanup INT TERM
16+
17+
bin/shakapacker "$@" &
18+
child_pid=$!
19+
20+
if wait "$child_pid"; then
21+
status=0
22+
else
23+
status=$?
24+
fi
25+
26+
child_pid=""
27+
exit "$status"

react_on_rails/lib/react_on_rails/dev/process_manager.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ def run_process_if_available(process, args)
9797

9898
# Process not available in either context
9999
nil
100+
rescue Interrupt
101+
# Ctrl-C during overmind/foreman shutdown should exit quietly.
102+
true
100103
end
101104

102105
# Run a process outside of bundler context

react_on_rails/spec/dummy/Procfile.dev

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# You can run these commands in separate shells
33
rails: bundle exec rails s -p 3000
44
dev-server: bin/shakapacker-dev-server
5-
server-bundle: SERVER_BUNDLE_ONLY=true bin/shakapacker --watch
5+
server-bundle: SERVER_BUNDLE_ONLY=true bin/shakapacker-watch --watch
66

77
# Bundle ReScript .res files
88
rescript: pnpm build:rescript:dev
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
web: bin/rails server -p 3000
2-
js: bin/shakapacker --watch
2+
js: bin/shakapacker-watch --watch
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Same as Procfile.dev, but disable turbolinks.
22
rails: DISABLE_TURBOLINKS=TRUE bin/rails s -p 3000
33
# Build client and server assets, watching for changes.
4-
webpack: sh -c 'rm -rf public/webpack/development/*' || true && bin/shakapacker --watch
4+
webpack: sh -c 'rm -rf public/webpack/development/*' || true && bin/shakapacker-watch --watch

react_on_rails/spec/react_on_rails/dev/process_manager_spec.rb

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
describe ".installed?" do
88
it "returns true when process is available in current context" do
99
expect(Timeout).to receive(:timeout).with(described_class::VERSION_CHECK_TIMEOUT).and_yield
10-
expect_any_instance_of(Kernel).to receive(:system)
10+
expect(described_class).to receive(:system)
1111
.with("overmind", "--version", out: File::NULL, err: File::NULL).and_return(true)
1212
expect(described_class).to be_installed("overmind")
1313
end
@@ -19,20 +19,20 @@
1919

2020
it "returns false when all version flags fail" do
2121
expect(Timeout).to receive(:timeout).with(described_class::VERSION_CHECK_TIMEOUT).exactly(3).times.and_yield
22-
expect_any_instance_of(Kernel).to receive(:system)
22+
expect(described_class).to receive(:system)
2323
.with("failing_process", "--version", out: File::NULL, err: File::NULL).and_return(false)
24-
expect_any_instance_of(Kernel).to receive(:system)
24+
expect(described_class).to receive(:system)
2525
.with("failing_process", "-v", out: File::NULL, err: File::NULL).and_return(false)
26-
expect_any_instance_of(Kernel).to receive(:system)
26+
expect(described_class).to receive(:system)
2727
.with("failing_process", "-V", out: File::NULL, err: File::NULL).and_return(false)
2828
expect(described_class.installed?("failing_process")).to be false
2929
end
3030

3131
it "returns true when second version flag succeeds" do
3232
expect(Timeout).to receive(:timeout).with(described_class::VERSION_CHECK_TIMEOUT).twice.and_yield
33-
expect_any_instance_of(Kernel).to receive(:system)
33+
expect(described_class).to receive(:system)
3434
.with("foreman", "--version", out: File::NULL, err: File::NULL).and_return(false)
35-
expect_any_instance_of(Kernel).to receive(:system)
35+
expect(described_class).to receive(:system)
3636
.with("foreman", "-v", out: File::NULL, err: File::NULL).and_return(true)
3737
expect(described_class.installed?("foreman")).to be true
3838
end
@@ -51,15 +51,15 @@
5151

5252
it "exits with error when Procfile does not exist" do
5353
allow(File).to receive(:exist?).with("Procfile.dev").and_return(false)
54-
expect_any_instance_of(Kernel).to receive(:exit).with(1)
54+
expect(described_class).to receive(:exit).with(1)
5555
described_class.ensure_procfile("Procfile.dev")
5656
end
5757
end
5858

5959
describe ".run_with_process_manager" do
6060
before do
6161
allow(ReactOnRails::Dev::FileManager).to receive(:cleanup_stale_files)
62-
allow_any_instance_of(Kernel).to receive(:system).and_return(true)
62+
allow(described_class).to receive(:system).and_return(true)
6363
allow(File).to receive(:readable?).and_return(true)
6464
end
6565

@@ -85,7 +85,7 @@
8585
expect(described_class).to receive(:run_process_if_available)
8686
.with("foreman", ["start", "-f", "Procfile.dev"]).and_return(nil)
8787
expect(described_class).not_to receive(:show_process_manager_installation_help)
88-
expect_any_instance_of(Kernel).to receive(:exit).with(1).and_raise(SystemExit)
88+
expect(described_class).to receive(:exit).with(1).and_raise(SystemExit)
8989

9090
expect { described_class.run_with_process_manager("Procfile.dev") }.to raise_error(SystemExit)
9191
end
@@ -96,7 +96,7 @@
9696
expect(described_class).to receive(:run_process_if_available)
9797
.with("foreman", ["start", "-f", "Procfile.dev"]).and_return(true)
9898
expect(described_class).not_to receive(:show_process_manager_installation_help)
99-
expect_any_instance_of(Kernel).not_to receive(:exit)
99+
expect(described_class).not_to receive(:exit)
100100

101101
described_class.run_with_process_manager("Procfile.dev")
102102
end
@@ -107,7 +107,7 @@
107107
expect(described_class).to receive(:run_process_if_available)
108108
.with("foreman", ["start", "-f", "Procfile.dev"]).and_return(false)
109109
expect(described_class).not_to receive(:show_process_manager_installation_help)
110-
expect_any_instance_of(Kernel).to receive(:exit).with(1).and_raise(SystemExit)
110+
expect(described_class).to receive(:exit).with(1).and_raise(SystemExit)
111111

112112
expect { described_class.run_with_process_manager("Procfile.dev") }.to raise_error(SystemExit)
113113
end
@@ -118,7 +118,7 @@
118118
expect(described_class).to receive(:run_process_if_available)
119119
.with("foreman", ["start", "-f", "Procfile.dev"]).and_return(false)
120120
expect(described_class).not_to receive(:show_process_manager_installation_help)
121-
expect_any_instance_of(Kernel).to receive(:exit).with(1).and_raise(SystemExit)
121+
expect(described_class).to receive(:exit).with(1).and_raise(SystemExit)
122122

123123
expect { described_class.run_with_process_manager("Procfile.dev") }.to raise_error(SystemExit)
124124
end
@@ -129,7 +129,7 @@
129129
expect(described_class).to receive(:run_process_if_available)
130130
.with("foreman", ["start", "-f", "Procfile.dev"]).and_return(nil)
131131
expect(described_class).to receive(:show_process_manager_installation_help)
132-
expect_any_instance_of(Kernel).to receive(:exit).with(1)
132+
expect(described_class).to receive(:exit).with(1)
133133

134134
described_class.run_with_process_manager("Procfile.dev")
135135
end
@@ -145,7 +145,15 @@
145145
describe ".run_process_if_available" do
146146
it "returns true and runs process when available in current context" do
147147
allow(described_class).to receive(:installed?).with("foreman").and_return(true)
148-
expect_any_instance_of(Kernel).to receive(:system).with("foreman", "start", "-f", "Procfile.dev").and_return(true)
148+
expect(described_class).to receive(:system).with("foreman", "start", "-f", "Procfile.dev").and_return(true)
149+
150+
result = described_class.send(:run_process_if_available, "foreman", ["start", "-f", "Procfile.dev"])
151+
expect(result).to be true
152+
end
153+
154+
it "returns true when interrupted while waiting on the process manager" do
155+
allow(described_class).to receive(:installed?).with("foreman").and_return(true)
156+
expect(described_class).to receive(:system).with("foreman", "start", "-f", "Procfile.dev").and_raise(Interrupt)
149157

150158
result = described_class.send(:run_process_if_available, "foreman", ["start", "-f", "Procfile.dev"])
151159
expect(result).to be true
@@ -173,14 +181,14 @@
173181
describe ".run_process_outside_bundle" do
174182
it "uses with_unbundled_context when Bundler is available" do
175183
expect(described_class).to receive(:with_unbundled_context).and_yield
176-
expect_any_instance_of(Kernel).to receive(:system).with("foreman", "start", "-f", "Procfile.dev")
184+
expect(described_class).to receive(:system).with("foreman", "start", "-f", "Procfile.dev")
177185

178186
described_class.send(:run_process_outside_bundle, "foreman", ["start", "-f", "Procfile.dev"])
179187
end
180188

181189
it "falls back to direct system call when Bundler is not available" do
182190
hide_const("Bundler")
183-
expect_any_instance_of(Kernel).to receive(:system).with("foreman", "start", "-f", "Procfile.dev")
191+
expect(described_class).to receive(:system).with("foreman", "start", "-f", "Procfile.dev")
184192

185193
described_class.send(:run_process_outside_bundle, "foreman", ["start", "-f", "Procfile.dev"])
186194
end
@@ -190,7 +198,7 @@
190198
it "checks process availability outside bundle context with version flags" do
191199
expect(described_class).to receive(:with_unbundled_context).and_yield
192200
expect(Timeout).to receive(:timeout).with(described_class::VERSION_CHECK_TIMEOUT).and_yield
193-
expect_any_instance_of(Kernel).to receive(:system)
201+
expect(described_class).to receive(:system)
194202
.with("foreman", "--version", out: File::NULL, err: File::NULL).and_return(true)
195203

196204
expect(described_class.send(:process_available_in_system?, "foreman")).to be true
@@ -205,9 +213,9 @@
205213
it "tries multiple version flags before failing" do
206214
expect(described_class).to receive(:with_unbundled_context).and_yield
207215
expect(Timeout).to receive(:timeout).with(described_class::VERSION_CHECK_TIMEOUT).twice.and_yield
208-
expect_any_instance_of(Kernel).to receive(:system)
216+
expect(described_class).to receive(:system)
209217
.with("foreman", "--version", out: File::NULL, err: File::NULL).and_return(false)
210-
expect_any_instance_of(Kernel).to receive(:system)
218+
expect(described_class).to receive(:system)
211219
.with("foreman", "-v", out: File::NULL, err: File::NULL).and_return(true)
212220

213221
expect(described_class.send(:process_available_in_system?, "foreman")).to be true

react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,21 @@ def simulate_managed_stock_webpack_files(options = {})
3636
end
3737
end
3838

39+
it "creates the shakapacker watch wrapper and uses it in Procfiles" do
40+
assert_file "bin/shakapacker-watch" do |content|
41+
expect(content).to include('bin/shakapacker "$@" &')
42+
expect(content).to include("trap cleanup INT TERM")
43+
end
44+
45+
assert_file "Procfile.dev" do |content|
46+
expect(content).to include("server-bundle: SERVER_BUNDLE_ONLY=yes bin/shakapacker-watch --watch")
47+
end
48+
49+
assert_file "Procfile.dev-static-assets" do |content|
50+
expect(content).to include("js: bin/shakapacker-watch --watch")
51+
end
52+
end
53+
3954
it "installs appropriate transpiler dependencies based on Shakapacker version" do
4055
assert_file "package.json" do |content|
4156
package_json = JSON.parse(content)
@@ -1413,6 +1428,7 @@ class ActiveSupport::TestCase
14131428
assert_file "Procfile.dev" do |content|
14141429
expect(content).to include("RSC_BUNDLE_ONLY=yes")
14151430
expect(content).to include("rsc-bundle:")
1431+
expect(content).to include("bin/shakapacker-watch --watch")
14161432
end
14171433
end
14181434

0 commit comments

Comments
 (0)