diff --git a/Cargo.lock b/Cargo.lock index 78fd977..f5d82a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1652,7 +1652,7 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "lesavka_client" -version = "0.16.6" +version = "0.16.7" dependencies = [ "anyhow", "async-stream", @@ -1686,7 +1686,7 @@ dependencies = [ [[package]] name = "lesavka_common" -version = "0.16.6" +version = "0.16.7" dependencies = [ "anyhow", "base64", @@ -1698,7 +1698,7 @@ dependencies = [ [[package]] name = "lesavka_server" -version = "0.16.6" +version = "0.16.7" dependencies = [ "anyhow", "base64", diff --git a/client/Cargo.toml b/client/Cargo.toml index 06542a5..d367ddf 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -4,7 +4,7 @@ path = "src/main.rs" [package] name = "lesavka_client" -version = "0.16.6" +version = "0.16.7" edition = "2024" [dependencies] diff --git a/common/Cargo.toml b/common/Cargo.toml index c41729e..08e585a 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lesavka_common" -version = "0.16.6" +version = "0.16.7" edition = "2024" build = "build.rs" diff --git a/scripts/install/server.sh b/scripts/install/server.sh index ea885a5..a46863f 100755 --- a/scripts/install/server.sh +++ b/scripts/install/server.sh @@ -1040,6 +1040,7 @@ sudo systemctl enable lesavka-core lesavka-server UDC_STATE=$(udc_state) FORCE_GADGET_REBUILD=0 GADGET_REBUILD_REASON="" +EXPLICIT_GADGET_REBUILD=0 if [[ -z ${LESAVKA_DISABLE_UVC:-} ]] && ! uvc_gadget_present; then FORCE_GADGET_REBUILD=1 GADGET_REBUILD_REASON="UVC function is missing from the live gadget" @@ -1052,11 +1053,13 @@ if [[ "$UVC_ENV_CHANGED" == "1" ]] && is_attached_state "$UDC_STATE"; then fi if [[ -n ${LESAVKA_FORCE_GADGET_REBUILD:-} ]]; then FORCE_GADGET_REBUILD=1 + EXPLICIT_GADGET_REBUILD=1 GADGET_REBUILD_REASON="explicit LESAVKA_FORCE_GADGET_REBUILD request" echo "⚠️ explicit LESAVKA_FORCE_GADGET_REBUILD request; forcing a gadget rebuild before server start." fi -if [[ "$FORCE_GADGET_REBUILD" == "1" && -z ${LESAVKA_ALLOW_GADGET_RESET:-} ]] && is_attached_state "$UDC_STATE"; then - echo "⚠️ ${GADGET_REBUILD_REASON:-Gadget state} requires a rebuild, but UDC state is '$UDC_STATE' and hard reset was not allowed." >&2 +if [[ "$FORCE_GADGET_REBUILD" == "1" ]] && is_attached_state "$UDC_STATE" \ + && { [[ "$EXPLICIT_GADGET_REBUILD" != "1" ]] || [[ -z ${LESAVKA_ALLOW_GADGET_RESET:-} ]]; }; then + echo "⚠️ ${GADGET_REBUILD_REASON:-Gadget state} requires a rebuild, but UDC state is '$UDC_STATE' and attached-host hard reset was not explicitly allowed." >&2 echo " Preserving the attached gadget to avoid wedging the Pi USB controller." >&2 echo " Run during a maintenance window with LESAVKA_ALLOW_GADGET_RESET=1 LESAVKA_FORCE_GADGET_REBUILD=1 if a hard rebuild is required." >&2 FORCE_GADGET_REBUILD=0 diff --git a/server/Cargo.toml b/server/Cargo.toml index e8d99da..71a44af 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -10,7 +10,7 @@ bench = false [package] name = "lesavka_server" -version = "0.16.6" +version = "0.16.7" edition = "2024" autobins = false diff --git a/testing/tests/server_install_script_contract.rs b/testing/tests/server_install_script_contract.rs index e7d70d9..198da6c 100644 --- a/testing/tests/server_install_script_contract.rs +++ b/testing/tests/server_install_script_contract.rs @@ -102,6 +102,14 @@ fn server_install_pins_hdmi_camera_and_display_defaults() { SERVER_INSTALL.contains("LESAVKA_FORCE_GADGET_REBUILD"), "install script should require an explicit force knob for attached-host gadget rebuilds" ); + assert!( + SERVER_INSTALL.contains("EXPLICIT_GADGET_REBUILD=1"), + "installer should distinguish organic rebuild needs from an explicit operator hard-reset request" + ); + assert!( + SERVER_INSTALL.contains("[[ \"$EXPLICIT_GADGET_REBUILD\" != \"1\" ]] || [[ -z ${LESAVKA_ALLOW_GADGET_RESET:-} ]]"), + "attached-host hard rebuilds should require both allow and explicit force flags" + ); assert!( SERVER_INSTALL.contains("no hard gadget rebuild is needed"), "LESAVKA_ALLOW_GADGET_RESET should permit recovery without causing unconditional hard rebuilds"