mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-11-02 05:17:23 +08:00
fix(cli): reinstall same-version frontend releases in dev mode
This commit is contained in:
@@ -357,6 +357,7 @@ moviepilot update all --skip-resources
|
||||
- `update backend` 会更新 Git 仓库并重新安装后端依赖,包括 `moviepilot-rust` 加速扩展
|
||||
- `MOVIEPILOT_UPDATE_DEV=true` 时,`moviepilot update` 默认使用 DEV 模式;`--dev` / `--no-dev` 可临时覆盖,省略目标时更新全部组件。进程环境变量优先于配置目录中的 `app.env`
|
||||
- DEV 模式后端跟踪当前开发分支,处于 Release 的 detached HEAD 时回到 `v3`;前端下载最新 Release 的 `dist.zip`。显式 `--ref` / `--frontend-version` 优先于默认选择
|
||||
- DEV 模式每次更新前端都会重新下载并替换发布包,即使版本号相同或显式指定了 `--frontend-version`,以获取同版本重新打包的内容
|
||||
- 非 DEV 模式下,`update frontend` 会按当前仓库 `version.py` 中的 `FRONTEND_VERSION` 下载并替换前端 release
|
||||
- `update all` 会先更新后端,再按所选模式更新前端,默认也会同步资源文件
|
||||
- 更新前请先执行 `moviepilot stop`
|
||||
|
||||
@@ -189,6 +189,7 @@ Usage:
|
||||
Options:
|
||||
--ref REF 后端 Git 版本,默认当前分支;DEV 下 detached HEAD 回到 v3
|
||||
--dev / --no-dev 指定 DEV 更新模式,默认读取 MOVIEPILOT_UPDATE_DEV
|
||||
DEV 模式下同版本前端也会重新下载并替换
|
||||
--frontend-version TAG 前端版本,DEV 默认 latest,否则使用 FRONTEND_VERSION
|
||||
--node-version VER 本地 Node 运行时版本,默认 20.12.1
|
||||
--python PYTHON 用于安装后端依赖的 Python 解释器,默认自动选择本地 3.14+ 版本
|
||||
|
||||
@@ -938,7 +938,10 @@ def install_frontend(
|
||||
frontend_version: str,
|
||||
node_version: str,
|
||||
archive: Optional[Path] = None,
|
||||
*,
|
||||
force: bool = False,
|
||||
) -> dict[str, str]:
|
||||
"""安装前端发布包;DEV 更新强制替换同版本重新打包的制品。"""
|
||||
if archive:
|
||||
version_tag = (frontend_version or "").strip()
|
||||
if not version_tag:
|
||||
@@ -950,7 +953,7 @@ def install_frontend(
|
||||
version_tag, download_url = _resolve_frontend_release(frontend_version)
|
||||
node_bin = install_node_runtime(node_version)
|
||||
|
||||
if _frontend_runtime_ready(version_tag):
|
||||
if not force and _frontend_runtime_ready(version_tag):
|
||||
_write_local_frontend_service_script(PUBLIC_DIR)
|
||||
print_step(f"前端发布包已是最新版本:{version_tag}")
|
||||
return {"version": version_tag, "node": str(node_bin)}
|
||||
@@ -3831,15 +3834,20 @@ def _update_backend_ref(ref: str, *, fetch: bool = True) -> str:
|
||||
return ref
|
||||
|
||||
|
||||
def _resolve_update_versions(args: argparse.Namespace) -> tuple[str, Optional[str]]:
|
||||
"""更新命令读取 Dev 偏好;显式版本优先,离线安装始终使用已确认制品。"""
|
||||
def _dev_update_enabled(args: argparse.Namespace) -> bool:
|
||||
"""按命令选项及配置判定 DEV 模式,离线安装不继承 DEV 偏好。"""
|
||||
value = os.environ.get("MOVIEPILOT_UPDATE_DEV")
|
||||
if value is None:
|
||||
value = read_env_value("MOVIEPILOT_UPDATE_DEV") or "false"
|
||||
dev = args.dev if args.dev is not None else value.strip().lower() in {
|
||||
"1", "true", "yes", "y", "on"
|
||||
}
|
||||
dev = dev and not args.offline_backend
|
||||
return dev and not args.offline_backend
|
||||
|
||||
|
||||
def _resolve_update_versions(args: argparse.Namespace) -> tuple[str, Optional[str]]:
|
||||
"""更新命令读取 Dev 偏好;显式版本优先,离线安装始终使用已确认制品。"""
|
||||
dev = _dev_update_enabled(args)
|
||||
ref = args.ref
|
||||
if not ref:
|
||||
ref = "latest"
|
||||
@@ -4308,6 +4316,7 @@ def main() -> int:
|
||||
frontend_version=frontend_version,
|
||||
node_version=args.node_version,
|
||||
archive=Path(args.frontend_archive) if args.frontend_archive else None,
|
||||
force=_dev_update_enabled(args),
|
||||
)
|
||||
print_step(f"前端更新完成,版本:{frontend_result['version']}")
|
||||
if args.target == "all" and not args.skip_resources:
|
||||
|
||||
@@ -152,3 +152,66 @@ def test_switching_to_dev_branch_fetches_latest_tip(monkeypatch, tmp_path, fetch
|
||||
assert module._update_backend_ref("v3", fetch=fetch) == "v3"
|
||||
pull_calls = [call.args[0] for call in run.call_args_list if call.args[0][:2] == ["git", "pull"]]
|
||||
assert pull_calls == ([["git", "pull", "--ff-only", "origin", "v3"]] if fetch and branch == "v3" else [])
|
||||
|
||||
|
||||
@pytest.mark.parametrize("target", ["frontend", "all"])
|
||||
@pytest.mark.parametrize("version_args", [[], ["--frontend-version", "v3.0.11"]])
|
||||
@pytest.mark.parametrize("saved_dev, mode_args, refresh", [
|
||||
("true", [], True),
|
||||
("false", ["--dev"], True),
|
||||
("true", ["--no-dev"], False),
|
||||
("false", [], False),
|
||||
])
|
||||
def test_update_reinstalls_repacked_frontend_in_dev_mode(
|
||||
monkeypatch, tmp_path, target, version_args, saved_dev, mode_args, refresh
|
||||
):
|
||||
"""连续更新同版本不同内容的 ZIP:DEV 真正替换文件,普通模式仍复用已安装版本。"""
|
||||
import zipfile
|
||||
|
||||
module = load_local_setup_module()
|
||||
public = tmp_path / "public"
|
||||
(public / "node_modules" / "express").mkdir(parents=True)
|
||||
(public / "version.txt").write_text("v3.0.11", encoding="utf-8")
|
||||
(public / "service.js").touch()
|
||||
(public / "index.html").write_text("original", encoding="utf-8")
|
||||
npm = tmp_path / "npm"
|
||||
npm.touch()
|
||||
monkeypatch.setattr(module, "PUBLIC_DIR", public)
|
||||
monkeypatch.delenv("MOVIEPILOT_UPDATE_DEV", raising=False)
|
||||
monkeypatch.setattr(module, "read_env_value", lambda _key: saved_dev)
|
||||
monkeypatch.setattr(module, "configure_config_dir", lambda **_: tmp_path)
|
||||
monkeypatch.setattr(module, "ensure_services_stopped", lambda: None)
|
||||
monkeypatch.setattr(module, "update_backend", lambda **_: None)
|
||||
monkeypatch.setattr(module, "_git_output", lambda *_: "v3")
|
||||
monkeypatch.setattr(module, "_repo_frontend_version", lambda: "v3.0.11")
|
||||
monkeypatch.setattr(module, "install_node_runtime", lambda _version: tmp_path / "node")
|
||||
monkeypatch.setattr(module, "get_npm_bin", lambda: npm)
|
||||
monkeypatch.setattr(module, "fetch_json", lambda _url: {
|
||||
"tag_name": "v3.0.11",
|
||||
"assets": [{"name": "dist.zip", "browser_download_url": "https://example.com/dist.zip"}],
|
||||
})
|
||||
monkeypatch.setattr(module.sys, "argv", [
|
||||
"local_setup.py", "update", *version_args, target, *mode_args, "--skip-resources"
|
||||
])
|
||||
downloads = []
|
||||
|
||||
def download_repacked_release(url, path):
|
||||
"""模拟同一个 Release URL 每次提供重新构建的内容。"""
|
||||
downloads.append(url)
|
||||
with zipfile.ZipFile(path, "w") as archive:
|
||||
archive.writestr("dist/version.txt", "v3.0.11")
|
||||
archive.writestr("dist/index.html", f"build-{len(downloads)}")
|
||||
|
||||
def install_runtime_deps(*_args, **_kwargs):
|
||||
"""恢复已安装依赖标记,保证第二次确实面对完整的同版本前端。"""
|
||||
(public / "node_modules" / "express").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
monkeypatch.setattr(module, "download_file", download_repacked_release)
|
||||
monkeypatch.setattr(module, "run", install_runtime_deps)
|
||||
for attempt in (1, 2):
|
||||
assert module._frontend_runtime_ready("v3.0.11")
|
||||
assert module.main() == 0
|
||||
assert (public / "index.html").read_text(encoding="utf-8") == (
|
||||
f"build-{attempt}" if refresh else "original"
|
||||
)
|
||||
assert downloads == (["https://example.com/dist.zip"] * 2 if refresh else [])
|
||||
|
||||
Reference in New Issue
Block a user