diff --git a/.changeset/docker-updates-tab.md b/.changeset/docker-updates-tab.md index 0ee7407..811478a 100644 --- a/.changeset/docker-updates-tab.md +++ b/.changeset/docker-updates-tab.md @@ -2,4 +2,4 @@ "@jmfederico/pi-web": patch --- -Expose Docker-aware PI WEB status, update, and restart commands in the Updates panel through the canonical `pi-web-docker` command, including explicit `pi-web-docker --dev ...` commands for Docker development runtimes, and harden production and development Docker workflows around generated Compose assets, Compose project-name isolation, root-safety checks, UID/GID preservation, and detached helper execution. +Expose Docker-aware PI WEB status, update, and restart commands in the Updates panel through the canonical `pi-web-docker` command, including explicit `pi-web-docker --dev ...` commands for Docker development runtimes, and harden production and development Docker workflows around generated Compose assets, Compose project-name isolation, clearer checkout/runtime guidance, root-safety checks, UID/GID preservation, and detached helper execution. diff --git a/docker/pi-web-docker b/docker/pi-web-docker index ec5d54e..2ecf8d4 100755 --- a/docker/pi-web-docker +++ b/docker/pi-web-docker @@ -250,11 +250,59 @@ docker_compose() { fi } +is_checkout_runtime_default_root() { + root=$1 + [ -z "${PI_WEB_DOCKER_INSTALL_DIR:-}" ] || return 1 + [ -f "$root/compose.dev.yml" ] || return 1 + [ -f "$root/../package.json" ] || return 1 + [ -f "$root/pi-web-docker" ] || return 1 +} + +runtime_command_hint() { + command=${command_name:-status} + printf '%s\n' "$command" +} + +default_runtime_entrypoint_hint() { + if [ -n "${XDG_DATA_HOME:-}" ]; then + printf '%s\n' "$XDG_DATA_HOME/pi-web-docker/pi-web-docker" + elif [ -n "${HOME:-}" ]; then + printf '%s\n' "$HOME/.local/share/pi-web-docker/pi-web-docker" + else + printf '%s\n' '~/.local/share/pi-web-docker/pi-web-docker' + fi +} + +die_missing_runtime_asset() { + root=$1 + missing_path=$2 + if is_checkout_runtime_default_root "$root"; then + command_hint=$(runtime_command_hint) + runtime_entrypoint=$(default_runtime_entrypoint_hint) + log "pi-web-docker: runtime install assets were not found in $root." + log "Missing generated asset: $missing_path" + log "" + log "You appear to be running this checkout's Docker command in runtime mode." + log "For development, use:" + log "" + log " ./docker/pi-web-docker --dev $command_hint" + log "" + log "For an installed runtime, use the installed command, usually:" + log "" + log " $runtime_entrypoint $command_hint" + log "" + log "Or set PI_WEB_DOCKER_INSTALL_DIR to your runtime install directory." + exit 1 + fi + + die "runtime install asset not found at $missing_path; run pi-web-docker install first" +} + require_runtime_compose_assets() { root=$1 - [ -f "$root/compose.yml" ] || die "runtime compose.yml not found at $root/compose.yml" - [ -f "$root/compose.override.yml" ] || die "runtime compose.override.yml not found at $root/compose.override.yml; run pi-web-docker install first" - [ -f "$root/.env" ] || die "runtime .env not found at $root/.env; run pi-web-docker install first" + [ -f "$root/compose.yml" ] || die_missing_runtime_asset "$root" "$root/compose.yml" + [ -f "$root/compose.override.yml" ] || die_missing_runtime_asset "$root" "$root/compose.override.yml" + [ -f "$root/.env" ] || die_missing_runtime_asset "$root" "$root/.env" } runtime_compose() { diff --git a/src/server/dockerControlAssets.test.ts b/src/server/dockerControlAssets.test.ts index 208ea0e..e044e5e 100644 --- a/src/server/dockerControlAssets.test.ts +++ b/src/server/dockerControlAssets.test.ts @@ -247,6 +247,23 @@ describe("Docker command assets", () => { expect(result.stdout).toContain("Usage: docker/install.sh [options]"); }); + it("explains source checkout runtime-mode mistakes", async () => { + const fakeDocker = await installFakeDocker(); + + const result = await runDockerCommandAllowFailure(["start"], { + ...cleanProcessEnv(), + PATH: `${fakeDocker.binDir}:${process.env["PATH"] ?? ""}`, + HOME: "/home/pi-web-test", + }); + + expect(result.exitCode).not.toBe(0); + expect(result.stderr).toContain("runtime install assets were not found"); + expect(result.stderr).toContain("running this checkout's Docker command in runtime mode"); + expect(result.stderr).toContain("./docker/pi-web-docker --dev start"); + expect(result.stderr).toContain("/home/pi-web-test/.local/share/pi-web-docker/pi-web-docker start"); + expect(result.stderr).toContain("PI_WEB_DOCKER_INSTALL_DIR"); + }); + it("starts restart-sessiond in a detached Docker helper", async () => { const installDir = await createRuntimeInstall(); const fakeDocker = await installFakeDocker();