mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-02 05:17:23 +08:00
fix: 修复更新重启工作目录
This commit is contained in:
@@ -294,6 +294,12 @@ function source_control_generation() {
|
||||
/entrypoint.sh --source-generation 2>/dev/null
|
||||
}
|
||||
|
||||
# 更新会原子替换 /app;重入前切到稳定目录,避免新 Bash 进程继承已删除的工作目录。
|
||||
function reexec_entrypoint() {
|
||||
cd / || exit 1
|
||||
exec /entrypoint.sh --post-update-reexec
|
||||
}
|
||||
|
||||
function maybe_reexec_control_bundle() {
|
||||
[ "${MOVIEPILOT_UPDATE_RESULT:-noop}" = "updated" ] || return 0
|
||||
|
||||
@@ -309,7 +315,7 @@ function maybe_reexec_control_bundle() {
|
||||
"${next_control_generation}" \
|
||||
"${MOVIEPILOT_BOOTSTRAP_REEXECUTED:-0}"; then
|
||||
INFO "→ 检测到容器控制脚本更新,使用新版本继续本次启动。"
|
||||
exec /entrypoint.sh --post-update-reexec
|
||||
reexec_entrypoint
|
||||
elif [ "$?" -eq 2 ]; then
|
||||
ERROR "→ 容器控制脚本在单次启动中重复变化,已终止以避免重启循环。"
|
||||
exit 1
|
||||
@@ -505,7 +511,7 @@ if [ "${MOVIEPILOT_BOOTSTRAP_UPDATE_DONE:-0}" != "1" ] \
|
||||
&& [ -f "${CONFIG_DIR}/temp/moviepilot-update/install.json" ]; then
|
||||
if apply_pending_release_update_at_startup; then
|
||||
INFO "→ 未完成的 Release 更新已安装,重新执行入口加载新代码。"
|
||||
exec /entrypoint.sh --post-update-reexec
|
||||
reexec_entrypoint
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -543,6 +549,10 @@ ensure_browser_kernel
|
||||
# 证书管理
|
||||
source "${MP_CONTROL_DIR:-/usr/local/lib/moviepilot/control}/cert.sh"
|
||||
|
||||
# /app 会被 Release worker 原子替换;常驻入口必须留在稳定目录,避免 worker 关闭 supervisor 后
|
||||
# 当前 shell 继承已删除的旧工作目录,导致重入时 Bash 连续报告 getcwd 错误。
|
||||
cd / || exit 1
|
||||
|
||||
# supervisord 常驻前台并统一托管 Nginx 与后端;带更新标记的 shutdown 会回到本入口消费更新包。
|
||||
install -d -m 0755 /run/moviepilot
|
||||
# Supervisor 的控制面只在容器内使用;未显式传入时生成本次容器启动专用的随机凭据,避免固定密码进入镜像。
|
||||
@@ -584,13 +594,13 @@ while true; do
|
||||
exit 1
|
||||
fi
|
||||
INFO "→ 更新代码已落盘,重新执行容器入口以加载新版本。"
|
||||
exec /entrypoint.sh --post-update-reexec
|
||||
reexec_entrypoint
|
||||
fi
|
||||
|
||||
if [ -f "${ONE_SHOT_DEV_UPDATE_FLAG}" ]; then
|
||||
if run_pending_dev_update_after_supervisor_shutdown; then
|
||||
INFO "→ 更新包已安装,重新执行容器入口以加载新版本。"
|
||||
exec /entrypoint.sh --post-update-reexec
|
||||
reexec_entrypoint
|
||||
fi
|
||||
if [ -f "${ONE_SHOT_DEV_UPDATE_FLAG}" ]; then
|
||||
ERROR "→ 更新请求未能完成且标记仍存在,停止启动。"
|
||||
|
||||
@@ -25,6 +25,12 @@ if ! "${VENV_PATH}/bin/python3" -m app.cli apply-prepared-update; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# /app 可能刚刚被替换,切回稳定目录后再调用 supervisor 控制面。
|
||||
if ! cd /; then
|
||||
ERROR "→ 更新后无法切回稳定工作目录。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! mkdir -p "$(dirname "${RESTART_REQUEST_FILE}")"; then
|
||||
ERROR "→ 无法记录更新后的重启请求。"
|
||||
exit 1
|
||||
|
||||
@@ -425,7 +425,8 @@ Python lifespan 会先撤销 readiness,再按组件声明的 `stop_order` 停
|
||||
普通应用重启通过本地 `supervisorctl restart all` 同时重启 Nginx 和后端。确认安装 Release 时,
|
||||
`SystemHelper` 只启动 root `moviepilot-update-worker`;worker 先替换 `/app`、`/public` 或站点资源,
|
||||
再写入 `moviepilot.pending_supervisor_restart` 并执行 `supervisorctl shutdown`。外层 entrypoint 看到标记后
|
||||
重新执行 launcher,加载新代码。Dev 更新仍通过一次性 Dev 标记关闭 Supervisor,再由 entrypoint 调用
|
||||
重新执行 launcher,加载新代码。载荷替换期间外层 entrypoint、worker 的控制面调用和所有入口重入都使用 `/`
|
||||
作为稳定工作目录,避免旧 `/app` 被删除后产生 `getcwd` 错误。Dev 更新仍通过一次性 Dev 标记关闭 Supervisor,再由 entrypoint 调用
|
||||
`update.sh`。这样更新包不会因只重启受管进程而停留在暂存目录;整个过程不访问 Docker API,也不依赖
|
||||
Docker restart policy。
|
||||
|
||||
|
||||
@@ -942,6 +942,24 @@ def test_release_update_worker_applies_before_supervisor_shutdown() -> None:
|
||||
assert worker.index("apply-prepared-update") < worker.index("shutdown")
|
||||
|
||||
|
||||
def test_update_reexec_uses_stable_working_directory() -> None:
|
||||
"""更新替换 /app 后,入口重入和 worker 控制面调用必须离开旧工作目录。"""
|
||||
entrypoint = (ROOT / "docker" / "entrypoint.sh").read_text(encoding="utf-8")
|
||||
worker = (ROOT / "docker" / "update-worker.sh").read_text(encoding="utf-8")
|
||||
|
||||
reexec = entrypoint.split("function reexec_entrypoint()", 1)[1].split("}", 1)[0]
|
||||
assert reexec.index("cd / || exit 1") < reexec.index("exec /entrypoint.sh")
|
||||
assert entrypoint.count("exec /entrypoint.sh --post-update-reexec") == 1
|
||||
assert entrypoint.count("reexec_entrypoint") == 5
|
||||
|
||||
supervisor_start = entrypoint.index("/usr/bin/supervisord -n")
|
||||
assert entrypoint.rfind("cd / || exit 1", 0, supervisor_start) > entrypoint.rfind(
|
||||
"cd /app || exit", 0, supervisor_start
|
||||
)
|
||||
worker_stable_cwd = worker.index("cd /", worker.index("apply-prepared-update"))
|
||||
assert worker_stable_cwd < worker.index("supervisorctl")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("pyproject_changed", "lock_changed", "expected_route_calls", "expected_sync_calls"),
|
||||
(
|
||||
|
||||
Reference in New Issue
Block a user