fix(docker): refuse unsafe development updates

This commit is contained in:
Pi Web Agent
2026-07-13 15:08:39 +00:00
parent 8b0452d545
commit c217c4a417
3 changed files with 164 additions and 2 deletions
+61
View File
@@ -25,6 +25,7 @@ Commands:
restart-web Restart only the web service
restart-sessiond Restart only the session daemon
update Rebuild/update and recreate the Docker stack
(development mode requires a clean Git checkout)
status Show Docker Compose service status
logs [web|sessiond|data-init]
Follow Docker Compose logs
@@ -229,6 +230,60 @@ enforce_dev_root_safety() {
[ "$uid" != 0 ] || die "refusing to run Docker development mode as root; retry with --allow-root if this is intentional"
}
dev_git_operation() {
git_dir=$1
if [ -f "$git_dir/MERGE_HEAD" ]; then
printf '%s\n' merge
elif [ -d "$git_dir/rebase-merge" ] || [ -d "$git_dir/rebase-apply" ] || [ -f "$git_dir/REBASE_HEAD" ]; then
printf '%s\n' rebase
elif [ -f "$git_dir/CHERRY_PICK_HEAD" ]; then
printf '%s\n' cherry-pick
elif [ -f "$git_dir/REVERT_HEAD" ]; then
printf '%s\n' revert
elif [ -d "$git_dir/sequencer" ]; then
printf '%s\n' sequenced-operation
elif [ -f "$git_dir/BISECT_LOG" ]; then
printf '%s\n' bisect
else
return 1
fi
}
require_clean_dev_update_checkout() {
[ "$(docker_mode)" = dev ] || return 0
root=$(dev_root)
require_command git
git_root=$(git -C "$root" rev-parse --show-toplevel 2>/dev/null) \
|| die "Docker development update requires a Git checkout at $root"
git_root=$(absolute_existing_dir "$git_root") \
|| die "could not resolve Git checkout root: $git_root"
[ "$git_root" = "$root" ] \
|| die "Docker development root $root must be the Git checkout root ($git_root)"
git_dir=$(git -C "$root" rev-parse --absolute-git-dir 2>/dev/null) \
|| die "could not resolve Git metadata for $root"
operation=$(dev_git_operation "$git_dir" 2>/dev/null || true)
if [ -n "$operation" ]; then
log "pi-web-docker: refusing to update the Docker development stack while a Git $operation is in progress: $root"
checkout_status=$(git -C "$root" status --porcelain=v1 --untracked-files=all 2>/dev/null || true)
if [ -n "$checkout_status" ]; then
log "Checkout status:"
printf '%s\n' "$checkout_status" >&2
fi
die "resolve or abort the Git $operation before rerunning pi-web-docker --dev update"
fi
checkout_status=$(git -C "$root" status --porcelain=v1 --untracked-files=all) \
|| die "could not inspect Git checkout status at $root"
if [ -n "$checkout_status" ]; then
log "pi-web-docker: refusing to update the Docker development stack because the checkout has uncommitted changes: $root"
log "Checkout status:"
printf '%s\n' "$checkout_status" >&2
die "commit, stash, or remove these changes before rerunning pi-web-docker --dev update; no files were changed"
fi
}
enforce_container_mode_match() {
is_truthy "${PI_WEB_DOCKER_RUNTIME:-}" || return 0
runtime_mode=${PI_WEB_DOCKER_MODE:-}
@@ -401,6 +456,7 @@ run_runtime_host_update() {
run_update() {
assert_no_args update "$@"
require_clean_dev_update_checkout
case "$(docker_mode)" in
runtime)
if ! is_truthy "${PI_WEB_DOCKER_RUNTIME:-}"; then
@@ -729,6 +785,11 @@ run_restart_or_update() {
shift
assert_no_args "$action" "$@"
if is_truthy "${PI_WEB_DOCKER_RUNTIME:-}"; then
# Fail before scheduling a helper, then recheck inside the helper in
# run_update so a checkout change cannot race the detached operation.
if [ "$action" = update ]; then
require_clean_dev_update_checkout
fi
start_detached_helper "$action"
return 0
fi