mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-04 23:17:20 +08:00
perf(docker): bound automatic update probes (#6357)
This commit is contained in:
+80
-37
@@ -91,20 +91,17 @@ function install_backend_and_download_resources() {
|
|||||||
if [ -f "${TMP_PATH}/App/requirements.in" ]; then
|
if [ -f "${TMP_PATH}/App/requirements.in" ]; then
|
||||||
if ! cmp -s /app/requirements.in "${TMP_PATH}/App/requirements.in"; then
|
if ! cmp -s /app/requirements.in "${TMP_PATH}/App/requirements.in"; then
|
||||||
INFO "检测到依赖变化,正在更新虚拟环境..."
|
INFO "检测到依赖变化,正在更新虚拟环境..."
|
||||||
# 备份当前requirements.txt
|
configure_pip_route
|
||||||
cp /app/requirements.txt /tmp/requirements.txt.backup
|
INFO "PIP:${PIP_LOG}"
|
||||||
# 复制新的requirements.in
|
local compiled_requirements="${TMP_PATH}/requirements.txt"
|
||||||
cp "${TMP_PATH}/App/requirements.in" /app/requirements.in
|
if ! env "${PIP_ENV[@]}" ${VENV_PATH}/bin/pip-compile \
|
||||||
# 重新编译依赖
|
"${TMP_PATH}/App/requirements.in" -o "${compiled_requirements}"; then
|
||||||
if ! env "${PIP_ENV[@]}" ${VENV_PATH}/bin/pip-compile /app/requirements.in -o /app/requirements.txt; then
|
ERROR "依赖编译失败,当前程序依赖未变更"
|
||||||
ERROR "依赖编译失败,恢复原依赖"
|
|
||||||
cp /tmp/requirements.txt.backup /app/requirements.txt
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
# 安装新依赖
|
if ! env "${PIP_ENV[@]}" ${VENV_PATH}/bin/pip install ${PIP_OPTIONS} \
|
||||||
if ! env "${PIP_ENV[@]}" ${VENV_PATH}/bin/pip install ${PIP_OPTIONS} -r /app/requirements.txt; then
|
-r "${compiled_requirements}"; then
|
||||||
ERROR "依赖安装失败,恢复原依赖"
|
ERROR "依赖安装失败,当前程序依赖清单未变更"
|
||||||
cp /tmp/requirements.txt.backup /app/requirements.txt
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
INFO "依赖更新成功"
|
INFO "依赖更新成功"
|
||||||
@@ -224,16 +221,54 @@ function install_backend_and_download_resources() {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function probe_pip_package() {
|
||||||
|
local probe_env=(
|
||||||
|
"UV_NO_CACHE=1"
|
||||||
|
"PIP_NO_CACHE_DIR=1"
|
||||||
|
"UV_HTTP_TIMEOUT=5"
|
||||||
|
"PIP_DEFAULT_TIMEOUT=5"
|
||||||
|
"UV_HTTP_RETRIES=0"
|
||||||
|
"PIP_RETRIES=0"
|
||||||
|
)
|
||||||
|
local package_index="${1:-}"
|
||||||
|
local use_proxy="${2:-false}"
|
||||||
|
local probe_dir
|
||||||
|
local -a probe_args=(install)
|
||||||
|
|
||||||
|
if [[ "${use_proxy}" = "true" ]]; then
|
||||||
|
probe_env+=(
|
||||||
|
"HTTP_PROXY=${PROXY_HOST}"
|
||||||
|
"HTTPS_PROXY=${PROXY_HOST}"
|
||||||
|
"http_proxy=${PROXY_HOST}"
|
||||||
|
"https_proxy=${PROXY_HOST}"
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
probe_dir=$(mktemp -d) || return 1
|
||||||
|
# 包源探针必须使用独立目标目录,避免修改主程序与插件共享的虚拟环境。
|
||||||
|
probe_args+=(--target "${probe_dir}" --no-deps)
|
||||||
|
if [[ -n "${package_index}" ]]; then
|
||||||
|
probe_args+=(-i "${package_index}")
|
||||||
|
fi
|
||||||
|
probe_args+=(pip-hello-world)
|
||||||
|
|
||||||
|
(
|
||||||
|
trap 'rm -rf "${probe_dir}"' EXIT
|
||||||
|
trap 'exit 129' HUP
|
||||||
|
trap 'exit 130' INT
|
||||||
|
trap 'exit 143' TERM
|
||||||
|
timeout --kill-after=2s 10s env "${probe_env[@]}" \
|
||||||
|
"${VENV_PATH}/bin/pip" "${probe_args[@]}" > /dev/null 2>&1
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function test_connectivity_pip() {
|
function test_connectivity_pip() {
|
||||||
${VENV_PATH}/bin/pip uninstall -y pip-hello-world > /dev/null 2>&1
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
0)
|
0)
|
||||||
if [[ -n "${PIP_PROXY}" ]]; then
|
if [[ -n "${PIP_PROXY}" ]]; then
|
||||||
if [[ -n "${PROXY_HOST}" ]]; then
|
if [[ -n "${PROXY_HOST}" ]]; then
|
||||||
HTTP_PROXY="${PROXY_HOST}" HTTPS_PROXY="${PROXY_HOST}" http_proxy="${PROXY_HOST}" https_proxy="${PROXY_HOST}" \
|
probe_pip_package "${PIP_PROXY}" true
|
||||||
${VENV_PATH}/bin/pip install -i ${PIP_PROXY} pip-hello-world > /dev/null 2>&1
|
|
||||||
else
|
else
|
||||||
${VENV_PATH}/bin/pip install -i ${PIP_PROXY} pip-hello-world > /dev/null 2>&1
|
probe_pip_package "${PIP_PROXY}" false
|
||||||
fi
|
fi
|
||||||
if [[ $? -eq 0 ]]; then
|
if [[ $? -eq 0 ]]; then
|
||||||
PIP_OPTIONS="-i ${PIP_PROXY}"
|
PIP_OPTIONS="-i ${PIP_PROXY}"
|
||||||
@@ -246,8 +281,7 @@ function test_connectivity_pip() {
|
|||||||
;;
|
;;
|
||||||
1)
|
1)
|
||||||
if [[ -n "${PROXY_HOST}" ]]; then
|
if [[ -n "${PROXY_HOST}" ]]; then
|
||||||
if HTTP_PROXY="${PROXY_HOST}" HTTPS_PROXY="${PROXY_HOST}" http_proxy="${PROXY_HOST}" https_proxy="${PROXY_HOST}" \
|
if probe_pip_package "" true; then
|
||||||
${VENV_PATH}/bin/pip install pip-hello-world > /dev/null 2>&1; then
|
|
||||||
PIP_OPTIONS=""
|
PIP_OPTIONS=""
|
||||||
PIP_LOG="全局代理模式"
|
PIP_LOG="全局代理模式"
|
||||||
set_package_proxy_env
|
set_package_proxy_env
|
||||||
@@ -270,7 +304,7 @@ function test_connectivity_github() {
|
|||||||
case "$1" in
|
case "$1" in
|
||||||
0)
|
0)
|
||||||
if [[ -n "${GITHUB_PROXY}" ]]; then
|
if [[ -n "${GITHUB_PROXY}" ]]; then
|
||||||
if curl -sL "${GITHUB_PROXY}https://raw.githubusercontent.com/jxxghp/MoviePilot/main/README.md" > /dev/null 2>&1; then
|
if curl -sL --connect-timeout 5 --max-time 10 "${GITHUB_PROXY}https://raw.githubusercontent.com/jxxghp/MoviePilot/main/README.md" > /dev/null 2>&1; then
|
||||||
GITHUB_LOG="镜像代理模式"
|
GITHUB_LOG="镜像代理模式"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -279,7 +313,7 @@ function test_connectivity_github() {
|
|||||||
;;
|
;;
|
||||||
1)
|
1)
|
||||||
if [[ -n "${PROXY_HOST}" ]]; then
|
if [[ -n "${PROXY_HOST}" ]]; then
|
||||||
if curl -sL -x ${PROXY_HOST} https://raw.githubusercontent.com/jxxghp/MoviePilot/main/README.md > /dev/null 2>&1; then
|
if curl -sL --connect-timeout 5 --max-time 10 -x ${PROXY_HOST} https://raw.githubusercontent.com/jxxghp/MoviePilot/main/README.md > /dev/null 2>&1; then
|
||||||
CURL_OPTIONS="-sL -x ${PROXY_HOST}"
|
CURL_OPTIONS="-sL -x ${PROXY_HOST}"
|
||||||
GITHUB_LOG="全局代理模式"
|
GITHUB_LOG="全局代理模式"
|
||||||
return 0
|
return 0
|
||||||
@@ -295,6 +329,30 @@ function test_connectivity_github() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function configure_pip_route() {
|
||||||
|
local retries=0
|
||||||
|
while true; do
|
||||||
|
if test_connectivity_pip "${retries}"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
retries=$((retries + 1))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetch_latest_v3_release() {
|
||||||
|
local response
|
||||||
|
local releases
|
||||||
|
local latest_release
|
||||||
|
|
||||||
|
response=$(curl ${CURL_OPTIONS} --compressed --fail --connect-timeout 5 --max-time 15 \
|
||||||
|
"https://api.github.com/repos/jxxghp/MoviePilot/releases" \
|
||||||
|
${CURL_HEADERS}) || return 1
|
||||||
|
releases=$(printf '%s\n' "${response}" | jq -r '.[].tag_name') || return 1
|
||||||
|
latest_release=$(printf '%s\n' "${releases}" | grep "^v3\." | sort -V | tail -n 1)
|
||||||
|
[[ -n "${latest_release}" ]] || return 1
|
||||||
|
printf '%s\n' "${latest_release}"
|
||||||
|
}
|
||||||
|
|
||||||
# 版本号比较
|
# 版本号比较
|
||||||
function compare_versions() {
|
function compare_versions() {
|
||||||
local v1="$1"
|
local v1="$1"
|
||||||
@@ -379,17 +437,6 @@ if [[ "${MOVIEPILOT_AUTO_UPDATE}" = "true" ]] || [[ "${MOVIEPILOT_AUTO_UPDATE}"
|
|||||||
fi
|
fi
|
||||||
mkdir -p /tmp/mp_update_path
|
mkdir -p /tmp/mp_update_path
|
||||||
fi
|
fi
|
||||||
# 优先级:镜像站 > 全局 > 不代理
|
|
||||||
# pip
|
|
||||||
retries=0
|
|
||||||
while true; do
|
|
||||||
if test_connectivity_pip ${retries}; then
|
|
||||||
break
|
|
||||||
else
|
|
||||||
retries=$((retries + 1))
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
# Github
|
|
||||||
retries=0
|
retries=0
|
||||||
while true; do
|
while true; do
|
||||||
if test_connectivity_github ${retries}; then
|
if test_connectivity_github ${retries}; then
|
||||||
@@ -398,7 +445,7 @@ if [[ "${MOVIEPILOT_AUTO_UPDATE}" = "true" ]] || [[ "${MOVIEPILOT_AUTO_UPDATE}"
|
|||||||
retries=$((retries + 1))
|
retries=$((retries + 1))
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
INFO "PIP:${PIP_LOG},Github:${GITHUB_LOG}"
|
INFO "Github:${GITHUB_LOG}"
|
||||||
if [ -n "${GITHUB_TOKEN}" ]; then
|
if [ -n "${GITHUB_TOKEN}" ]; then
|
||||||
CURL_HEADERS="--oauth2-bearer ${GITHUB_TOKEN}"
|
CURL_HEADERS="--oauth2-bearer ${GITHUB_TOKEN}"
|
||||||
else
|
else
|
||||||
@@ -415,13 +462,9 @@ if [[ "${MOVIEPILOT_AUTO_UPDATE}" = "true" ]] || [[ "${MOVIEPILOT_AUTO_UPDATE}"
|
|||||||
if [[ "${old_version}" == *APP_VERSION* ]]; then
|
if [[ "${old_version}" == *APP_VERSION* ]]; then
|
||||||
current_version=$(echo "${old_version}" | sed -rn "s/APP_VERSION\s*=\s*['\"](.*)['\"]/\1/gp")
|
current_version=$(echo "${old_version}" | sed -rn "s/APP_VERSION\s*=\s*['\"](.*)['\"]/\1/gp")
|
||||||
INFO "当前版本号:${current_version}"
|
INFO "当前版本号:${current_version}"
|
||||||
# 获取所有发布的版本列表,并筛选出以v3开头的版本号
|
if ! latest_v3=$(fetch_latest_v3_release); then
|
||||||
releases=$(curl ${CURL_OPTIONS} "https://api.github.com/repos/jxxghp/MoviePilot/releases" ${CURL_HEADERS} | jq -r '.[].tag_name' | grep "^v3\.")
|
|
||||||
if [ -z "$releases" ]; then
|
|
||||||
WARN "未找到任何v3后端版本,继续启动..."
|
WARN "未找到任何v3后端版本,继续启动..."
|
||||||
else
|
else
|
||||||
# 找到最新的v3版本
|
|
||||||
latest_v3=$(echo "$releases" | sort -V | tail -n 1)
|
|
||||||
INFO "最新的v3后端版本号:${latest_v3}"
|
INFO "最新的v3后端版本号:${latest_v3}"
|
||||||
# 使用版本号比较函数进行比较,并下载最新版本
|
# 使用版本号比较函数进行比较,并下载最新版本
|
||||||
compare_versions "${current_version}" "${latest_v3}"
|
compare_versions "${current_version}" "${latest_v3}"
|
||||||
|
|||||||
@@ -602,6 +602,301 @@ def test_updater_exposes_explicit_result(
|
|||||||
assert result.stdout == f"{expected}\n"
|
assert result.stdout == f"{expected}\n"
|
||||||
|
|
||||||
|
|
||||||
|
def test_release_noop_preserves_prerelease_selection_without_probing_package_index(
|
||||||
|
tmp_path: Path,
|
||||||
|
) -> None:
|
||||||
|
pip_probe = tmp_path / "pip-probe"
|
||||||
|
curl_log = tmp_path / "curl.log"
|
||||||
|
comparison_log = tmp_path / "comparison.log"
|
||||||
|
script = textwrap.dedent(
|
||||||
|
f"""\
|
||||||
|
CONFIG_DIR="$1"
|
||||||
|
MOVIEPILOT_AUTO_UPDATE=release
|
||||||
|
PIP_PROXY= PROXY_HOST= GITHUB_PROXY= GITHUB_TOKEN=
|
||||||
|
PIP_PROBE="$2"
|
||||||
|
CURL_LOG="$3"
|
||||||
|
COMPARISON_LOG="$4"
|
||||||
|
source {UPDATER!s}
|
||||||
|
INFO() {{ :; }}
|
||||||
|
WARN() {{ :; }}
|
||||||
|
ERROR() {{ :; }}
|
||||||
|
test_connectivity_pip() {{ touch "${{PIP_PROBE}}"; return 0; }}
|
||||||
|
test_connectivity_github() {{ CURL_OPTIONS=-sL; GITHUB_LOG=test; return 0; }}
|
||||||
|
compare_versions() {{ printf '%s|%s\n' "$1" "$2" > "${{COMPARISON_LOG}}"; return 1; }}
|
||||||
|
grep() {{
|
||||||
|
if [[ "$*" == *"/app/version.py"* ]]; then
|
||||||
|
printf '%s\n' "APP_VERSION = 'v3.0.0'"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
command grep "$@"
|
||||||
|
}}
|
||||||
|
sed() {{
|
||||||
|
if [[ "$*" == *"APP_VERSION"* ]]; then
|
||||||
|
printf '%s\n' 'v3.0.0'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
command sed "$@"
|
||||||
|
}}
|
||||||
|
curl() {{
|
||||||
|
printf '%s\n' "$*" >> "${{CURL_LOG}}"
|
||||||
|
printf '%s\n' '[{{"tag_name":"v2.15.6"}},{{"tag_name":"v3.1.0-beta"}},{{"tag_name":"v3.1.0-rc"}},{{"tag_name":"v3.0.0"}}]'
|
||||||
|
}}
|
||||||
|
run_moviepilot_update
|
||||||
|
printf '%s\n' "${{MOVIEPILOT_UPDATE_RESULT}}"
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
[
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
script,
|
||||||
|
"release-noop-test",
|
||||||
|
str(tmp_path / "config"),
|
||||||
|
str(pip_probe),
|
||||||
|
str(curl_log),
|
||||||
|
str(comparison_log),
|
||||||
|
],
|
||||||
|
text=True,
|
||||||
|
capture_output=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.stdout == "noop\n"
|
||||||
|
assert not pip_probe.exists()
|
||||||
|
curl_args = curl_log.read_text(encoding="utf-8")
|
||||||
|
assert "/releases" in curl_args
|
||||||
|
assert "/releases/latest" not in curl_args
|
||||||
|
assert "--compressed" in curl_args
|
||||||
|
assert "--fail" in curl_args
|
||||||
|
assert "--connect-timeout 5" in curl_args
|
||||||
|
assert "--max-time 15" in curl_args
|
||||||
|
assert comparison_log.read_text(encoding="utf-8") == "v3.0.0|v3.1.0-rc\n"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("dependencies_changed", "expected_route_calls", "expected_install_calls"),
|
||||||
|
((False, 0, 0), (True, 1, 1)),
|
||||||
|
)
|
||||||
|
def test_package_route_is_only_configured_for_changed_dependencies(
|
||||||
|
tmp_path: Path,
|
||||||
|
dependencies_changed: bool,
|
||||||
|
expected_route_calls: int,
|
||||||
|
expected_install_calls: int,
|
||||||
|
) -> None:
|
||||||
|
venv_bin = tmp_path / "venv" / "bin"
|
||||||
|
venv_bin.mkdir(parents=True)
|
||||||
|
pip_log = tmp_path / "pip.log"
|
||||||
|
route_log = tmp_path / "route.log"
|
||||||
|
for executable in ("pip", "pip-compile"):
|
||||||
|
path = venv_bin / executable
|
||||||
|
path.write_text(
|
||||||
|
"#!/bin/bash\nprintf '%s\\n' \"$*\" >> \"${PIP_TEST_LOG}\"\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
path.chmod(0o755)
|
||||||
|
update_tree = tmp_path / "update" / "App"
|
||||||
|
update_tree.mkdir(parents=True)
|
||||||
|
(update_tree / "requirements.in").write_text("new-package==1\n", encoding="utf-8")
|
||||||
|
(update_tree / "version.py").write_text("FRONTEND_VERSION = ''\n", encoding="utf-8")
|
||||||
|
script = textwrap.dedent(
|
||||||
|
f"""\
|
||||||
|
CONFIG_DIR="$1"
|
||||||
|
VENV_PATH="$2"
|
||||||
|
TMP_PATH="$3"
|
||||||
|
ROUTE_LOG="$4"
|
||||||
|
DEPENDENCIES_CHANGED="$5"
|
||||||
|
PIP_PROXY= PROXY_HOST=
|
||||||
|
source {UPDATER!s}
|
||||||
|
INFO() {{ :; }}
|
||||||
|
WARN() {{ :; }}
|
||||||
|
ERROR() {{ :; }}
|
||||||
|
download_and_unzip() {{ return 0; }}
|
||||||
|
cmp() {{ [ "${{DEPENDENCIES_CHANGED}}" = false ]; }}
|
||||||
|
cp() {{ return 0; }}
|
||||||
|
configure_pip_route() {{ printf 'route\n' >> "${{ROUTE_LOG}}"; PIP_LOG=test; }}
|
||||||
|
install_backend_and_download_resources tags/v3.0.1.zip || true
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
env = {**os.environ, "PIP_TEST_LOG": str(pip_log)}
|
||||||
|
result = subprocess.run(
|
||||||
|
[
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
script,
|
||||||
|
"package-route-test",
|
||||||
|
str(tmp_path / "config"),
|
||||||
|
str(tmp_path / "venv"),
|
||||||
|
str(tmp_path / "update"),
|
||||||
|
str(route_log),
|
||||||
|
str(dependencies_changed).lower(),
|
||||||
|
],
|
||||||
|
text=True,
|
||||||
|
capture_output=True,
|
||||||
|
check=True,
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result.stderr == ""
|
||||||
|
route_calls = route_log.read_text(encoding="utf-8").splitlines() if route_log.exists() else []
|
||||||
|
install_calls = pip_log.read_text(encoding="utf-8").splitlines() if pip_log.exists() else []
|
||||||
|
assert len(route_calls) == expected_route_calls
|
||||||
|
compile_calls = [call for call in install_calls if not call.startswith("install ")]
|
||||||
|
package_install_calls = [call for call in install_calls if call.startswith("install ")]
|
||||||
|
assert len(package_install_calls) == expected_install_calls
|
||||||
|
if dependencies_changed:
|
||||||
|
assert compile_calls == [
|
||||||
|
f"{update_tree / 'requirements.in'} -o {tmp_path / 'update' / 'requirements.txt'}"
|
||||||
|
]
|
||||||
|
assert package_install_calls == [
|
||||||
|
f"install -r {tmp_path / 'update' / 'requirements.txt'}"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("failure", ("compile", "install", "post_install"))
|
||||||
|
def test_failed_dependency_update_does_not_overwrite_current_manifests(
|
||||||
|
tmp_path: Path,
|
||||||
|
failure: str,
|
||||||
|
) -> None:
|
||||||
|
venv_bin = tmp_path / "venv" / "bin"
|
||||||
|
venv_bin.mkdir(parents=True)
|
||||||
|
command_log = tmp_path / "commands.log"
|
||||||
|
copy_log = tmp_path / "copies.log"
|
||||||
|
for executable in ("pip", "pip-compile"):
|
||||||
|
path = venv_bin / executable
|
||||||
|
path.write_text(
|
||||||
|
"#!/bin/bash\n"
|
||||||
|
'printf \'%s|%s\\n\' "$(basename "$0")" "$*" >> "${COMMAND_LOG}"\n'
|
||||||
|
f'[[ "$(basename "$0")" == "pip-compile" && "{failure}" == "compile" ]] && exit 1\n'
|
||||||
|
f'[[ "$(basename "$0")" == "pip" && "{failure}" == "install" ]] && exit 1\n'
|
||||||
|
"exit 0\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
path.chmod(0o755)
|
||||||
|
update_tree = tmp_path / "update" / "App"
|
||||||
|
update_tree.mkdir(parents=True)
|
||||||
|
(update_tree / "requirements.in").write_text("new-package==1\n", encoding="utf-8")
|
||||||
|
(update_tree / "version.py").write_text(
|
||||||
|
"FRONTEND_VERSION = 'v3.0.0'\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
script = textwrap.dedent(
|
||||||
|
f"""\
|
||||||
|
CONFIG_DIR="$1"
|
||||||
|
VENV_PATH="$2"
|
||||||
|
TMP_PATH="$3"
|
||||||
|
COPY_LOG="$4"
|
||||||
|
PIP_PROXY= PROXY_HOST=
|
||||||
|
FAILURE={failure}
|
||||||
|
source {UPDATER!s}
|
||||||
|
INFO() {{ :; }}
|
||||||
|
WARN() {{ :; }}
|
||||||
|
ERROR() {{ :; }}
|
||||||
|
download_and_unzip() {{
|
||||||
|
if [[ "${{FAILURE}}" = post_install ]] && [[ "$2" = dist ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}}
|
||||||
|
cmp() {{ return 1; }}
|
||||||
|
cp() {{ printf '%s\n' "$*" >> "${{COPY_LOG}}"; }}
|
||||||
|
configure_pip_route() {{ PIP_LOG=test; }}
|
||||||
|
install_backend_and_download_resources tags/v3.0.1.zip || true
|
||||||
|
if [[ "${{FAILURE}}" = post_install ]]; then
|
||||||
|
install_backend_and_download_resources tags/v3.0.1.zip || true
|
||||||
|
fi
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
script,
|
||||||
|
"dependency-failure-test",
|
||||||
|
str(tmp_path / "config"),
|
||||||
|
str(tmp_path / "venv"),
|
||||||
|
str(tmp_path / "update"),
|
||||||
|
str(copy_log),
|
||||||
|
],
|
||||||
|
text=True,
|
||||||
|
capture_output=True,
|
||||||
|
check=True,
|
||||||
|
env={**os.environ, "COMMAND_LOG": str(command_log)},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert not copy_log.exists()
|
||||||
|
commands = command_log.read_text(encoding="utf-8").splitlines()
|
||||||
|
assert commands[0] == (
|
||||||
|
f"pip-compile|{update_tree / 'requirements.in'} "
|
||||||
|
f"-o {tmp_path / 'update' / 'requirements.txt'}"
|
||||||
|
)
|
||||||
|
assert all("/app/requirements" not in command for command in commands)
|
||||||
|
if failure == "compile":
|
||||||
|
assert len(commands) == 1
|
||||||
|
elif failure == "install":
|
||||||
|
assert commands[1] == f"pip|install -r {tmp_path / 'update' / 'requirements.txt'}"
|
||||||
|
else:
|
||||||
|
assert commands == [
|
||||||
|
(
|
||||||
|
f"pip-compile|{update_tree / 'requirements.in'} "
|
||||||
|
f"-o {tmp_path / 'update' / 'requirements.txt'}"
|
||||||
|
),
|
||||||
|
f"pip|install -r {tmp_path / 'update' / 'requirements.txt'}",
|
||||||
|
(
|
||||||
|
f"pip-compile|{update_tree / 'requirements.in'} "
|
||||||
|
f"-o {tmp_path / 'update' / 'requirements.txt'}"
|
||||||
|
),
|
||||||
|
f"pip|install -r {tmp_path / 'update' / 'requirements.txt'}",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_package_index_probe_is_cacheless_and_bounded(tmp_path: Path) -> None:
|
||||||
|
timeout_log = tmp_path / "timeout.log"
|
||||||
|
script = textwrap.dedent(
|
||||||
|
f"""\
|
||||||
|
CONFIG_DIR="$1"
|
||||||
|
VENV_PATH="$2"
|
||||||
|
TIMEOUT_LOG="$3"
|
||||||
|
PIP_PROXY=https://packages.example/simple
|
||||||
|
PROXY_HOST=
|
||||||
|
source {UPDATER!s}
|
||||||
|
timeout() {{ printf '%s\n' "$*" > "${{TIMEOUT_LOG}}"; return 124; }}
|
||||||
|
test_connectivity_pip 0 || true
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
"bash",
|
||||||
|
"-c",
|
||||||
|
script,
|
||||||
|
"package-probe-test",
|
||||||
|
str(tmp_path / "config"),
|
||||||
|
str(tmp_path / "venv"),
|
||||||
|
str(timeout_log),
|
||||||
|
],
|
||||||
|
text=True,
|
||||||
|
capture_output=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
command = timeout_log.read_text(encoding="utf-8")
|
||||||
|
assert command.startswith("--kill-after=2s 10s env ")
|
||||||
|
assert "UV_NO_CACHE=1" in command
|
||||||
|
assert "PIP_NO_CACHE_DIR=1" in command
|
||||||
|
assert "UV_HTTP_TIMEOUT=5" in command
|
||||||
|
assert "PIP_DEFAULT_TIMEOUT=5" in command
|
||||||
|
assert "UV_HTTP_RETRIES=0" in command
|
||||||
|
assert "PIP_RETRIES=0" in command
|
||||||
|
assert "pip install --target " in command
|
||||||
|
assert " --no-deps -i https://packages.example/simple pip-hello-world" in command
|
||||||
|
assert "uninstall" not in command
|
||||||
|
probe_dir = Path(command.split("--target ", 1)[1].split(" ", 1)[0])
|
||||||
|
assert not probe_dir.exists()
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("result", "current", "next_generation", "guard", "expected"),
|
("result", "current", "next_generation", "guard", "expected"),
|
||||||
(
|
(
|
||||||
|
|||||||
Reference in New Issue
Block a user