Repository navigation
Expand file tree
/
Copy pathstart_dev.sh
More file actions
executable file
·200 lines (182 loc) · 6.34 KB
/
Copy pathstart_dev.sh
File metadata and controls
executable file
·200 lines (182 loc) · 6.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#!/usr/bin/env bash
# Frontend development: nac-web serving the API plus the Vite dev server with
# React Fast Refresh in front of it.
#
# Vite owns the browser origin and proxies the API routes to nac-web, so the app
# is same-origin and needs no CORS handling. Open the Vite URL, not the API one.
#
# ./start_dev.sh
# API http://127.0.0.1:3210
# app http://localhost:5173 (opens in the browser)
#
# The dev server also enables LocatorJS: hold Alt and click a rendered element to
# open its source. It is absent from the committed build that ./start.sh serves.
#
# Environment:
# DEV_BIND address nac-web binds to (default 127.0.0.1:3210)
# DEV_STORE_PATH optional authoritative store-path override
# VITE_HOST Vite host (default 127.0.0.1)
# VITE_PORT port for the Vite dev server (default 5173)
# NAC_PROFILE cargo profile for nac-web: debug (default) or release
# DEV_OPEN 1 to open the Vite URL, 0 to leave it terminal-only
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$ROOT"
BIND="${DEV_BIND:-127.0.0.1:3210}"
VITE_PORT="${VITE_PORT:-5173}"
VITE_HOST="${VITE_HOST:-127.0.0.1}"
PROFILE="${NAC_PROFILE:-debug}"
DEV_OPEN="${DEV_OPEN:-1}"
WEB_DIR="crates/nac-server/web"
for tool in cargo npm curl; do
if ! command -v "$tool" >/dev/null 2>&1; then
echo "start_dev.sh: $tool not found." >&2
case "$tool" in
cargo) echo "Install Rust from https://rustup.rs" >&2 ;;
npm) echo "Install Node.js from https://nodejs.org" >&2 ;;
esac
exit 1
fi
done
case "$PROFILE" in
release) CARGO_PROFILE_ARGS=(--release) ;;
debug) CARGO_PROFILE_ARGS=() ;;
*)
echo "start_dev.sh: NAC_PROFILE must be 'release' or 'debug', got '$PROFILE'" >&2
exit 1
;;
esac
echo "==> building nac-web ($PROFILE)"
# The `${arr[@]+...}` guard keeps an empty array from tripping `set -u` on the
# bash 3.2 that ships with macOS.
"${CARGO:-cargo}" build --locked ${CARGO_PROFILE_ARGS[@]+"${CARGO_PROFILE_ARGS[@]}"} \
-p nac-server --bin nac-web
BACKEND_PID=""
VITE_PID=""
cleanup_started=false
job_is_running() {
local wanted="$1"
local running
for running in $(jobs -pr); do
[[ "$running" == "$wanted" ]] && return 0
done
return 1
}
process_group_is_running() {
local pid="$1"
kill -0 "-$pid" 2>/dev/null
}
terminate_group() {
local pid="$1"
[[ -n "$pid" ]] || return 0
# The job leader may already have exited while one of its descendants is
# still alive. Probe the process group itself so cleanup still reaches those
# descendants instead of relying on Bash's now-completed job table entry.
if process_group_is_running "$pid"; then
kill -TERM "-$pid" 2>/dev/null || true
for _ in $(seq 1 50); do
process_group_is_running "$pid" || break
sleep 0.1
done
if process_group_is_running "$pid"; then
kill -KILL "-$pid" 2>/dev/null || true
fi
fi
wait "$pid" 2>/dev/null || true
}
cleanup() {
[[ "$cleanup_started" == false ]] || return 0
cleanup_started=true
terminate_group "$VITE_PID"
terminate_group "$BACKEND_PID"
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
# Non-interactive Bash otherwise keeps every background child in this script's
# process group. Monitor mode gives each child its own group, so cleanup reaches
# cargo/npm descendants on both supported developer platforms.
set -m
API_URL="http://$BIND"
APP_URL="http://$VITE_HOST:$VITE_PORT"
if curl -fsS --noproxy '*' --connect-timeout 1 --max-time 2 -- "$API_URL/health" >/dev/null 2>&1; then
echo "start_dev.sh: NAC is already responding at $API_URL" >&2
exit 1
fi
if curl -fsS --noproxy '*' --connect-timeout 1 --max-time 2 -- "$APP_URL/" >/dev/null 2>&1; then
echo "start_dev.sh: another frontend is already responding at $APP_URL" >&2
exit 1
fi
echo "==> Rust API: $API_URL"
# The browser should open on the Vite origin below, not the API bind.
# `-y` skips the project-folder prompt so a backgrounded API never blocks.
SERVER_ARGS=(--bind "$BIND" --no-open -y)
if [[ -n "${DEV_STORE_PATH:-}" ]]; then
SERVER_ARGS+=(--store-path "$DEV_STORE_PATH")
fi
"${NAC_DEV_SERVER_BIN:-target/$PROFILE/nac-web}" "${SERVER_ARGS[@]}" &
BACKEND_PID=$!
# Vite would otherwise start proxying to a socket that is not listening yet and
# the first requests would fail with a connection error.
backend_ready=false
for _ in $(seq 1 150); do
if curl -fsS --noproxy '*' --connect-timeout 1 --max-time 2 -- "$API_URL/health" >/dev/null 2>&1; then
backend_ready=true
break
fi
if ! job_is_running "$BACKEND_PID"; then
echo "start_dev.sh: nac-web exited during startup" >&2
if wait "$BACKEND_PID"; then exit 1; else exit $?; fi
fi
sleep 0.2
done
if [[ "$backend_ready" != true ]]; then
echo "start_dev.sh: timed out waiting for $API_URL/health" >&2
exit 1
fi
echo "==> Vite/HMR app: $APP_URL"
# Vite runs in the background so that a signal reaching this script is handled
# right away instead of after the foreground child returns. `--open` opens the
# app origin (not the API) once Vite is ready — same idea as Storybook.
VITE_ARGS=(--host "$VITE_HOST" --port "$VITE_PORT")
if [[ "$DEV_OPEN" == 1 ]]; then
VITE_ARGS+=(--open)
elif [[ "$DEV_OPEN" != 0 ]]; then
echo "start_dev.sh: DEV_OPEN must be 0 or 1, got '$DEV_OPEN'" >&2
exit 2
fi
NAC_API_URL="$API_URL" \
npm --prefix "$WEB_DIR" run dev -- "${VITE_ARGS[@]}" &
VITE_PID=$!
frontend_ready=false
for _ in $(seq 1 150); do
if curl -fsS --noproxy '*' --connect-timeout 1 --max-time 2 -- "$APP_URL/" >/dev/null 2>&1; then
frontend_ready=true
break
fi
if ! job_is_running "$BACKEND_PID"; then
echo "start_dev.sh: nac-web exited while Vite was starting" >&2
if wait "$BACKEND_PID"; then exit 1; else exit $?; fi
fi
if ! job_is_running "$VITE_PID"; then
echo "start_dev.sh: Vite exited during startup" >&2
if wait "$VITE_PID"; then exit 1; else exit $?; fi
fi
sleep 0.2
done
if [[ "$frontend_ready" != true ]]; then
echo "start_dev.sh: timed out waiting for $APP_URL/" >&2
exit 1
fi
echo "==> development stack ready; press Ctrl-C to stop both process trees"
while true; do
if ! job_is_running "$BACKEND_PID"; then
echo "start_dev.sh: nac-web exited; stopping Vite" >&2
if wait "$BACKEND_PID"; then exit 1; else exit $?; fi
fi
if ! job_is_running "$VITE_PID"; then
echo "start_dev.sh: Vite exited; stopping nac-web" >&2
if wait "$VITE_PID"; then exit 1; else exit $?; fi
fi
sleep 0.1
done