mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-13 16:54:48 +00:00
Stabilize hot-dev watcher restarts
This commit is contained in:
parent
e87c2629ea
commit
3f6c910f3a
3 changed files with 52 additions and 5 deletions
|
|
@ -153,6 +153,13 @@ to be listening.
|
||||||
Changes to `scripts/hot-dev.sh` and `scripts/hot-dev-bg.sh` must therefore
|
Changes to `scripts/hot-dev.sh` and `scripts/hot-dev-bg.sh` must therefore
|
||||||
stay on their own direct dev-runtime orchestration proof path instead of
|
stay on their own direct dev-runtime orchestration proof path instead of
|
||||||
piggybacking on installer proof coverage for unrelated deployment scripts.
|
piggybacking on installer proof coverage for unrelated deployment scripts.
|
||||||
|
That same dev-runtime orchestration boundary also owns watcher stability for
|
||||||
|
the managed local stack: `scripts/hot-dev.sh` may only rebuild the backend for
|
||||||
|
runtime Go sources, not `*_test.go` churn, and it must suppress `pulse` binary
|
||||||
|
change events produced by its own successful managed rebuilds or startup build.
|
||||||
|
Otherwise unrelated parallel test edits or hot-dev's own binary output can
|
||||||
|
tear down `7655`, produce transient `5173` proxy failures, and undermine the
|
||||||
|
canonical browser-runtime proof path.
|
||||||
That same launcher boundary also owns its CLI contract: managed commands such
|
That same launcher boundary also owns its CLI contract: managed commands such
|
||||||
as `start --takeover` and `restart --takeover` must preserve the takeover flag
|
as `start --takeover` and `restart --takeover` must preserve the takeover flag
|
||||||
through the actual script entrypoint instead of silently dropping second-arg
|
through the actual script entrypoint instead of silently dropping second-arg
|
||||||
|
|
|
||||||
|
|
@ -514,8 +514,9 @@ log_info "Starting backend file watcher..."
|
||||||
(
|
(
|
||||||
cd "${ROOT_DIR}"
|
cd "${ROOT_DIR}"
|
||||||
|
|
||||||
LAST_RESTART_TIME=0
|
|
||||||
WATCHER_READY_AT=$(date +%s)
|
WATCHER_READY_AT=$(date +%s)
|
||||||
|
LAST_RESTART_TIME=0
|
||||||
|
SELF_BUILD_IGNORE_UNTIL=$((WATCHER_READY_AT + HOT_DEV_WATCHER_STARTUP_GRACE_SECONDS + 5))
|
||||||
|
|
||||||
watcher_within_startup_grace() {
|
watcher_within_startup_grace() {
|
||||||
local now
|
local now
|
||||||
|
|
@ -523,6 +524,27 @@ log_info "Starting backend file watcher..."
|
||||||
(( now - WATCHER_READY_AT < HOT_DEV_WATCHER_STARTUP_GRACE_SECONDS ))
|
(( now - WATCHER_READY_AT < HOT_DEV_WATCHER_STARTUP_GRACE_SECONDS ))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mark_self_build_output() {
|
||||||
|
local now
|
||||||
|
now=$(date +%s)
|
||||||
|
SELF_BUILD_IGNORE_UNTIL=$((now + 5))
|
||||||
|
}
|
||||||
|
|
||||||
|
manual_build_event_suppressed() {
|
||||||
|
local now
|
||||||
|
now=$(date +%s)
|
||||||
|
(( now < SELF_BUILD_IGNORE_UNTIL ))
|
||||||
|
}
|
||||||
|
|
||||||
|
should_rebuild_backend_for_change() {
|
||||||
|
local changed_file=$1
|
||||||
|
|
||||||
|
[[ "${changed_file}" == *.go ]] || return 1
|
||||||
|
[[ "${changed_file}" == *_test.go ]] && return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
restart_backend() {
|
restart_backend() {
|
||||||
# Debounce: skip if we restarted less than 3 seconds ago
|
# Debounce: skip if we restarted less than 3 seconds ago
|
||||||
local now
|
local now
|
||||||
|
|
@ -604,6 +626,7 @@ log_info "Starting backend file watcher..."
|
||||||
|
|
||||||
# Use the same build logic as the initial build.
|
# Use the same build logic as the initial build.
|
||||||
if build_backend_binary; then
|
if build_backend_binary; then
|
||||||
|
mark_self_build_output
|
||||||
restart_backend
|
restart_backend
|
||||||
else
|
else
|
||||||
log_error "✗ Build failed; keeping the current backend process running."
|
log_error "✗ Build failed; keeping the current backend process running."
|
||||||
|
|
@ -627,9 +650,12 @@ log_info "Starting backend file watcher..."
|
||||||
log_info "🚀 Managed restart requested, restarting backend..."
|
log_info "🚀 Managed restart requested, restarting backend..."
|
||||||
restart_backend
|
restart_backend
|
||||||
elif [[ "$changed_file" == "${ROOT_DIR}/pulse" ]]; then
|
elif [[ "$changed_file" == "${ROOT_DIR}/pulse" ]]; then
|
||||||
|
if manual_build_event_suppressed; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
log_info "🚀 Manual build detected (pulse binary changed), restarting..."
|
log_info "🚀 Manual build detected (pulse binary changed), restarting..."
|
||||||
restart_backend
|
restart_backend
|
||||||
elif [[ "$changed_file" == *.go ]] || [[ "$event" =~ CREATE|DELETE|MOVED|ATTRIB ]]; then
|
elif should_rebuild_backend_for_change "$changed_file"; then
|
||||||
rebuild_backend "$changed_file"
|
rebuild_backend "$changed_file"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
@ -651,6 +677,9 @@ log_info "Starting backend file watcher..."
|
||||||
if [[ "$changed_file" == "${HOT_DEV_RESTART_SENTINEL}" ]]; then
|
if [[ "$changed_file" == "${HOT_DEV_RESTART_SENTINEL}" ]]; then
|
||||||
log_info "🚀 Managed restart requested, restarting backend..."
|
log_info "🚀 Managed restart requested, restarting backend..."
|
||||||
else
|
else
|
||||||
|
if manual_build_event_suppressed; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
log_info "🚀 Manual build detected (pulse binary changed), restarting..."
|
log_info "🚀 Manual build detected (pulse binary changed), restarting..."
|
||||||
fi
|
fi
|
||||||
restart_backend
|
restart_backend
|
||||||
|
|
@ -663,8 +692,7 @@ log_info "Starting backend file watcher..."
|
||||||
--include '\.go$' \
|
--include '\.go$' \
|
||||||
"${ROOT_DIR}/cmd" "${ROOT_DIR}/internal" "${ROOT_DIR}/pkg" 2>/dev/null | \
|
"${ROOT_DIR}/cmd" "${ROOT_DIR}/internal" "${ROOT_DIR}/pkg" 2>/dev/null | \
|
||||||
while read -r changed_file; do
|
while read -r changed_file; do
|
||||||
# fswatch sends absolute paths, simple check for .go extension
|
if should_rebuild_backend_for_change "$changed_file"; then
|
||||||
if [[ "$changed_file" == *.go ]]; then
|
|
||||||
rebuild_backend "$changed_file"
|
rebuild_backend "$changed_file"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ set -euo pipefail
|
||||||
|
|
||||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
HOT_DEV_BG="${ROOT_DIR}/scripts/hot-dev-bg.sh"
|
HOT_DEV_BG="${ROOT_DIR}/scripts/hot-dev-bg.sh"
|
||||||
|
HOT_DEV="${ROOT_DIR}/scripts/hot-dev.sh"
|
||||||
CLEAN_MOCK_ALERTS="${ROOT_DIR}/scripts/clean-mock-alerts.sh"
|
CLEAN_MOCK_ALERTS="${ROOT_DIR}/scripts/clean-mock-alerts.sh"
|
||||||
DEV_CHECK="${ROOT_DIR}/scripts/dev-check.sh"
|
DEV_CHECK="${ROOT_DIR}/scripts/dev-check.sh"
|
||||||
PACKAGE_JSON="${ROOT_DIR}/package.json"
|
PACKAGE_JSON="${ROOT_DIR}/package.json"
|
||||||
|
|
@ -468,7 +469,7 @@ test_makefile_routes_managed_runtime_through_npm() {
|
||||||
|
|
||||||
test_hot_dev_script_advertises_foreground_escape_hatch() {
|
test_hot_dev_script_advertises_foreground_escape_hatch() {
|
||||||
local output
|
local output
|
||||||
output="$(sed -n '1,30p' "${ROOT_DIR}/scripts/hot-dev.sh")"
|
output="$(sed -n '1,30p' "${HOT_DEV}")"
|
||||||
|
|
||||||
assert_contains "hot-dev header identifies foreground escape hatch" "${output}" "hot-dev.sh - Foreground Pulse dev runtime escape hatch"
|
assert_contains "hot-dev header identifies foreground escape hatch" "${output}" "hot-dev.sh - Foreground Pulse dev runtime escape hatch"
|
||||||
assert_contains "hot-dev usage points to managed runtime first" "${output}" "npm run dev # Canonical managed dev runtime"
|
assert_contains "hot-dev usage points to managed runtime first" "${output}" "npm run dev # Canonical managed dev runtime"
|
||||||
|
|
@ -476,6 +477,16 @@ test_hot_dev_script_advertises_foreground_escape_hatch() {
|
||||||
assert_not_contains "hot-dev usage no longer claims standard dev mode" "${output}" "Standard dev mode"
|
assert_not_contains "hot-dev usage no longer claims standard dev mode" "${output}" "Standard dev mode"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test_hot_dev_script_ignores_test_only_backend_churn() {
|
||||||
|
local output
|
||||||
|
output="$(cat "${HOT_DEV}")"
|
||||||
|
|
||||||
|
assert_contains "hot-dev watcher ignores Go test files" "${output}" '[[ "${changed_file}" == *_test.go ]] && return 1'
|
||||||
|
assert_contains "hot-dev watcher routes rebuild decisions through shared helper" "${output}" 'should_rebuild_backend_for_change "$changed_file"'
|
||||||
|
assert_contains "hot-dev watcher suppresses self-build binary restart loops" "${output}" 'if manual_build_event_suppressed; then'
|
||||||
|
assert_contains "hot-dev watcher suppresses startup self-build pulse events" "${output}" 'SELF_BUILD_IGNORE_UNTIL=$((WATCHER_READY_AT + HOT_DEV_WATCHER_STARTUP_GRACE_SECONDS + 5))'
|
||||||
|
}
|
||||||
|
|
||||||
test_hot_dev_bg_script_advertises_managed_entrypoint() {
|
test_hot_dev_bg_script_advertises_managed_entrypoint() {
|
||||||
local output
|
local output
|
||||||
output="$(cat "${ROOT_DIR}/scripts/hot-dev-bg.sh")"
|
output="$(cat "${ROOT_DIR}/scripts/hot-dev-bg.sh")"
|
||||||
|
|
@ -744,6 +755,7 @@ main() {
|
||||||
test_frontend_package_exposes_managed_runtime_entrypoints
|
test_frontend_package_exposes_managed_runtime_entrypoints
|
||||||
test_makefile_routes_managed_runtime_through_npm
|
test_makefile_routes_managed_runtime_through_npm
|
||||||
test_hot_dev_script_advertises_foreground_escape_hatch
|
test_hot_dev_script_advertises_foreground_escape_hatch
|
||||||
|
test_hot_dev_script_ignores_test_only_backend_churn
|
||||||
test_hot_dev_bg_script_advertises_managed_entrypoint
|
test_hot_dev_bg_script_advertises_managed_entrypoint
|
||||||
test_hot_dev_bg_usage_prefers_managed_wrappers
|
test_hot_dev_bg_usage_prefers_managed_wrappers
|
||||||
test_integration_readme_uses_managed_backend_restart_wrapper
|
test_integration_readme_uses_managed_backend_restart_wrapper
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue