From 71b83195ac763d17593c6f1b1f583961033e0d74 Mon Sep 17 00:00:00 2001 From: ThearchyHelios Date: Mon, 19 Oct 2026 16:15:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=8D=E6=A0=B8=20R-d6=EF=BC=88sudo=20./depl?= =?UTF-8?q?oy.sh=20=E6=97=B6=20DATA=5FDIR=3D~/=E2=80=A6=20=E5=B1=95?= =?UTF-8?q?=E5=BC=80=E6=88=90=20/root=20=E4=B8=8B=EF=BC=89=EF=BC=9A?= =?UTF-8?q?=E6=9C=89=20SUDO=5FUSER=20=E6=97=B6=20~=20=E6=8C=89=E5=AE=83?= =?UTF-8?q?=E7=9A=84=E5=AE=B6=E7=9B=AE=E5=BD=95=E5=B1=95=E5=BC=80=EF=BC=88?= =?UTF-8?q?getent=20passwd=20=E5=8F=96=EF=BC=8C=E5=8F=96=E4=B8=8D=E5=88=B0?= =?UTF-8?q?=E9=80=80=E5=9B=9E=20$HOME=EF=BC=89=EF=BC=8C=E3=80=8C=E4=B8=8D?= =?UTF-8?q?=E8=83=BD=E6=98=AF=E5=AE=B6=E7=9B=AE=E5=BD=95=E6=9C=AC=E8=BA=AB?= =?UTF-8?q?=E3=80=8D=E4=B9=9F=E6=8C=89=E5=AE=83=E6=9F=A5=EF=BC=9Binstall?= =?UTF-8?q?=E3=80=81upgrade=E3=80=81migrate=20=E5=BC=80=E5=A4=B4=E6=89=93?= =?UTF-8?q?=E5=8D=B0=E5=B1=95=E5=BC=80=E5=90=8E=E7=9A=84=E7=BB=9D=E5=AF=B9?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=EF=BC=88DATA=5FDIR=20=E7=94=A8=E4=BA=86=20~?= =?UTF-8?q?=20=E6=97=B6=E6=B3=A8=E6=98=8E=20~=20=E6=98=AF=E5=93=AA?= =?UTF-8?q?=E4=B8=AA=E7=9B=AE=E5=BD=95=EF=BC=89=E3=80=82deploy/README=20?= =?UTF-8?q?=E5=86=99=E4=B8=80=E5=8F=A5=EF=BC=88agentlab.env=20=E5=B7=B2?= =?UTF-8?q?=E6=9C=89=E7=9A=84=E8=A1=8C=E6=B2=A1=E5=8A=A8=EF=BC=89=E3=80=82?= =?UTF-8?q?=E5=81=87=20docker=20=E5=8D=95=E6=B5=8B=EF=BC=9ASUDO=5FUSER=20?= =?UTF-8?q?=E7=9A=84=E5=AE=B6=E7=9B=AE=E5=BD=95=E3=80=81=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E6=97=B6=E9=80=80=E5=9B=9E=20$HOME?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/tests/test_deploy_script.py | 21 +++++++++++++++++++++ deploy/README.md | 4 ++++ deploy/deploy.sh | 21 ++++++++++++++++++--- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/backend/tests/test_deploy_script.py b/backend/tests/test_deploy_script.py index 022f33a3..35e92103 100644 --- a/backend/tests/test_deploy_script.py +++ b/backend/tests/test_deploy_script.py @@ -301,6 +301,27 @@ def test_more_than_one_worker_needs_postgres(tmp_path): assert deploy.containers()[APP]["state"] == "running", "内置 PostgreSQL 时可以多进程" +def test_tilde_in_data_dir_is_the_sudo_users_home(tmp_path): + """复核 R-d6:DATA_DIR=~/… 用 sudo 运行时按 SUDO_USER 的家目录展开(getent passwd 取),不是 /root;展开后的绝对路径打印出来。""" + deploy = Deploy(tmp_path, env_lines=["DATA_DIR=~/agentlab_data"]) + alice = tmp_path / "home" / "alice" + alice.mkdir(parents=True) + root_home = tmp_path / "root" + root_home.mkdir() + getent = tmp_path / "bin" / "getent" + getent.write_text(f'#!/bin/sh\n[ "$1" = passwd ] && [ "$2" = alice ] && echo "alice:x:1000:1000::{alice}:/bin/bash"\n') + getent.chmod(0o755) + out = deploy("install", HOME=str(root_home), SUDO_USER="alice") + target = (alice / "agentlab_data").resolve() + assert target.is_dir() and not (root_home / "agentlab_data").exists() + assert f"数据目录:{target}(DATA_DIR=~/agentlab_data,~ 是 {alice})" in out.stdout + assert deploy.containers()[APP]["mounts"]["/data"] == str(target) + # 取不到 SUDO_USER 的家目录:退回 $HOME + other = Deploy(tmp_path / "other", env_lines=["DATA_DIR=~/agentlab_data"], name="agl-b") + other("install", HOME=str(root_home), SUDO_USER="nobody-here") + assert (root_home / "agentlab_data").is_dir() + + def test_install_postgres_refuses_a_leftover_volume(deploy): deploy.state_file.write_text(json.dumps({"images": {}, "containers": {}, "networks": [], "volumes": [VOL]})) out = deploy("install", "--postgres", ok=False) diff --git a/deploy/README.md b/deploy/README.md index cae696f4..a31a944c 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -85,6 +85,10 @@ sudo cp -rp data/backups/sqlite/<时间>/records/. data/records/ # 记 `-wal`、`-shm` 要先删:留着的话 SQLite 会把它们当成换回来的库的一部分。 +`DATA_DIR` 写成 `~/…` 时,`~` 是运行 deploy.sh 的那个人的家目录:用 `sudo ./deploy.sh` 运行时按调用 sudo 的用户 +(`SUDO_USER`)的家目录展开,不是 `/root`。`install`、`upgrade` 开头会打印展开后的绝对路径(`==> 数据目录:…`),对一眼; +不放心就直接写绝对路径。 + deploy.sh 会把数据目录的属主改成容器里的用户(uid 10001),所以备份、删除要用 sudo。 停下来备份最稳妥(以默认的 `./data` 为例,在包目录里执行): diff --git a/deploy/deploy.sh b/deploy/deploy.sh index dd0cd98a..842c81eb 100755 --- a/deploy/deploy.sh +++ b/deploy/deploy.sh @@ -215,15 +215,29 @@ read_env() { case "$ENABLE_MICROVM" in auto|0|1) ;; *) die "ENABLE_MICROVM 只能是 auto、0 或 1:$ENABLE_MICROVM" ;; esac # 相对路径以 deploy.sh 所在的目录为准,和从哪里运行无关 - # 下面比较的就是字面上的 ~,不需要展开 + # 下面比较的就是字面上的 ~,不需要展开。~ 是运行 deploy.sh 的那个人的家目录:sudo ./deploy.sh 时 $HOME 常是 /root, + # 按 SUDO_USER 的家目录展开(见 invoking_home) + INVOKER_HOME="$(invoking_home)" + TILDE_NOTE="" # shellcheck disable=SC2088 case "$DATA_DIR" in /*) DATA_PATH="$DATA_DIR" ;; - "~"|"~/"*) DATA_PATH="$HOME${DATA_DIR#\~}" ;; + "~"|"~/"*) + DATA_PATH="$INVOKER_HOME${DATA_DIR#\~}" + TILDE_NOTE="(DATA_DIR=${DATA_DIR},~ 是 ${INVOKER_HOME})" ;; *) DATA_PATH="$HERE/${DATA_DIR#./}" ;; esac } +# 家目录:用 sudo 运行时是调用 sudo 的那个人的(getent passwd 取),取不到退回 $HOME +invoking_home() { + local home="" + if [ -n "${SUDO_USER:-}" ] && [ "$SUDO_USER" != root ] && command -v getent >/dev/null 2>&1; then + home="$(getent passwd "$SUDO_USER" 2>/dev/null | cut -d: -f6 || true)" + fi + printf '%s' "${home:-$HOME}" +} + # --------------------------------------------------------------------------- # docker 与镜像 # --------------------------------------------------------------------------- @@ -307,7 +321,8 @@ prepare_data() { /|/bin|/boot|/dev|/etc|/home|/lib|/opt|/proc|/root|/sbin|/srv|/sys|/usr|/var) die "DATA_DIR 不能是系统目录:$DATA_ABS" ;; esac - [ "$DATA_ABS" != "$HOME" ] || die "DATA_DIR 不能是家目录本身:$DATA_ABS" + [ "$DATA_ABS" != "$HOME" ] && [ "$DATA_ABS" != "${INVOKER_HOME:-$HOME}" ] || die "DATA_DIR 不能是家目录本身:$DATA_ABS" + say "数据目录:${DATA_ABS}${TILDE_NOTE:-}" } # 已经有同名容器时:它得是 AgentLab 的,数据目录也得和配置的是同一个。