From 49118eeef211d856a2b27ca8138705f2bb250b24 Mon Sep 17 00:00:00 2001 From: ThearchyHelios Date: Mon, 19 Oct 2026 16:13:35 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=8D=E6=A0=B8=20R-d5=EF=BC=88dev.sh=20?= =?UTF-8?q?=E8=AF=BB=20.env=20=E4=B8=8D=E5=85=A8=EF=BC=89=EF=BC=9A?= =?UTF-8?q?=E8=AE=A4=20KEY=20=3D=20value=EF=BC=88=E7=AD=89=E5=8F=B7?= =?UTF-8?q?=E4=B8=A4=E8=BE=B9=E6=9C=89=E7=A9=BA=E6=A0=BC=EF=BC=8C=E5=92=8C?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=E7=9A=84=20dotenv=20=E4=B8=80=E8=87=B4?= =?UTF-8?q?=EF=BC=89=EF=BC=9B=E6=94=BE=E5=BC=80=E5=8F=91=E5=BA=93=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E7=9A=84=E6=95=B0=E6=8D=AE=E7=9B=AE=E5=BD=95=E4=B9=9F?= =?UTF-8?q?=E7=85=A7=E3=80=8C=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F=20>=20ba?= =?UTF-8?q?ckend/.env=20>=20.env=E3=80=8D=E8=AF=BB=20AGENTLAB=5FDATA=5FDIR?= =?UTF-8?q?=EF=BC=8C=E7=9B=B8=E5=AF=B9=E8=B7=AF=E5=BE=84=E6=8C=89=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=E7=9A=84=E5=B7=A5=E4=BD=9C=E7=9B=AE=E5=BD=95=20backen?= =?UTF-8?q?d/=20=E7=AE=97=EF=BC=8C.postgres-dev=20=E8=90=BD=E5=9C=A8?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=E5=AE=9E=E9=99=85=E7=94=A8=E7=9A=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=9B=AE=E5=BD=95=E4=B8=8B=E3=80=82=E5=8D=95=E6=B5=8B?= =?UTF-8?q?=EF=BC=9A=E7=AD=89=E5=8F=B7=E5=B8=A6=E7=A9=BA=E6=A0=BC=E3=80=81?= =?UTF-8?q?export=E3=80=81=E8=A1=8C=E5=B0=BE=E6=B3=A8=E9=87=8A=E3=80=81bac?= =?UTF-8?q?kend/.env=20=E4=BC=98=E5=85=88=E3=80=81=E7=9B=B8=E5=AF=B9?= =?UTF-8?q?=E7=9A=84=E6=95=B0=E6=8D=AE=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 3 ++- backend/tests/test_deploy_script.py | 18 +++++++++++------ scripts/dev.sh | 31 ++++++++++++++++++----------- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/.env.example b/.env.example index 582ab6c4..a32e27f5 100644 --- a/.env.example +++ b/.env.example @@ -53,7 +53,8 @@ # ---- 开发用 PostgreSQL(./scripts/dev.sh --postgres)---- # 这几项只有 dev.sh 读:写成环境变量或写在这个 .env 里都行(环境变量优先)。数据在 docker 数据卷里, -# 密码第一次自动生成在 data/.postgres-dev/,dev.sh 照它设 AGENTLAB_DATABASE_URL(盖过上面写的) +# 密码第一次自动生成在数据目录(和后端一样照 AGENTLAB_DATA_DIR,缺省 data/)的 .postgres-dev/ 里, +# dev.sh 照它设 AGENTLAB_DATABASE_URL(盖过上面写的) # AGENTLAB_DEV_PG_CONTAINER=agentlab-dev-postgres # AGENTLAB_DEV_PG_PORT=55432 # AGENTLAB_DEV_PG_VOLUME=agentlab-dev-pgdata diff --git a/backend/tests/test_deploy_script.py b/backend/tests/test_deploy_script.py index 77dcc803..022f33a3 100644 --- a/backend/tests/test_deploy_script.py +++ b/backend/tests/test_deploy_script.py @@ -813,7 +813,7 @@ def test_env_example_lists_every_database_pool_and_multiprocess_setting(): names = {f"AGENTLAB_{name.upper()}" for name in Settings.model_fields if name.startswith(prefixes)} assert {"AGENTLAB_DATABASE_URL", "AGENTLAB_DATABASE_DIRECT_URL", "AGENTLAB_DB_PGBOUNCER", "AGENTLAB_WORKERS", "AGENTLAB_WORKER_LEASE_S", "AGENTLAB_DB_STARTUP_TIMEOUT", "AGENTLAB_SCHEDULER_TICK_S"} <= names - dev = set(re.findall(r"dev_pg_setting (AGENTLAB_DEV_PG_[A-Z_]+)", (REPO / "scripts" / "dev.sh").read_text())) + dev = set(re.findall(r"dotenv_setting (AGENTLAB_DEV_PG_[A-Z_]+)", (REPO / "scripts" / "dev.sh").read_text())) assert len(dev) == 4 example = (REPO / ".env.example").read_text(encoding="utf-8") listed = set(re.findall(r"^# (AGENTLAB_[A-Z_]+)=", example, re.M)) @@ -827,9 +827,12 @@ def test_dev_script_reads_its_postgres_settings_from_dotenv(tmp_path): (root / "scripts").mkdir(parents=True) (root / "frontend" / "node_modules").mkdir(parents=True) shutil.copy(REPO / "scripts" / "dev.sh", root / "scripts" / "dev.sh") - (root / "backend").symlink_to(REPO / "backend") - (root / ".env").write_text("AGENTLAB_DEV_PG_CONTAINER=from-root-env\nAGENTLAB_DEV_PG_PORT=55491\n" - "AGENTLAB_DEV_PG_VOLUME='quoted-vol' \n") + (root / ".env").write_text("AGENTLAB_DEV_PG_CONTAINER=from-root-env\nAGENTLAB_DEV_PG_PORT = 55491 # 注释\n" + "export AGENTLAB_DEV_PG_VOLUME = 'quoted-vol' \nAGENTLAB_DATA_DIR=from-root\n") + # backend/.env 比 .env 优先(同后端):数据目录写相对路径,按后端的工作目录 backend/ 算 + (root / "backend").mkdir() + (root / "backend" / ".env").write_text("AGENTLAB_DATA_DIR = ../devdata\n") + (root / "backend" / "app").symlink_to(REPO / "backend" / "app") bin_dir = tmp_path / "bin" bin_dir.mkdir() log = tmp_path / "docker.log" @@ -839,7 +842,7 @@ def test_dev_script_reads_its_postgres_settings_from_dotenv(tmp_path): 'container) exit 1 ;; run) exit 1 ;; esac\nexit 0\n') for f in bin_dir.iterdir(): f.chmod(0o755) - env = {"PATH": f"{bin_dir}:/usr/bin:/bin:/usr/sbin:/sbin", "HOME": str(tmp_path), "AGENTLAB_DATA_DIR": str(tmp_path / "data"), + env = {"PATH": f"{bin_dir}:/usr/bin:/bin:/usr/sbin:/sbin", "HOME": str(tmp_path), "AGENTLAB_DEV_PG_IMAGE": "postgres:from-env", "AGENTLAB_PORT": "18799", "LANG": "C.UTF-8"} out = subprocess.run(["bash", str(root / "scripts" / "dev.sh"), "--postgres"], capture_output=True, text=True, env=env, timeout=60) @@ -848,7 +851,10 @@ def test_dev_script_reads_its_postgres_settings_from_dotenv(tmp_path): assert "--name from-root-env" in run and "127.0.0.1:55491:5432" in run, ".env 里的生效" assert "quoted-vol:/var/lib/postgresql/data" in run, "引号和行尾空白去掉" assert run.endswith("postgres:from-env"), "环境变量优先" - (root / "backend").unlink() + data = root / "backend" / ".." / "devdata" + assert f"{data}/.postgres-dev/password:/run/agentlab/password:ro" in run, "密码放在后端实际用的数据目录里" + assert (root / "devdata" / ".postgres-dev" / "pgpass").exists() + (root / "backend" / "app").unlink() def test_dev_script_refuses_unknown_arguments_and_documents_postgres(): diff --git a/scripts/dev.sh b/scripts/dev.sh index 4ed44005..328bafb4 100755 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -73,19 +73,24 @@ fi export AGENTLAB_PORT="$API_PORT" # ---- --postgres:本地开发用的 PostgreSQL ---- -# 这几项(AGENTLAB_DEV_PG_*)只有这个脚本用。和后端的配置一样认 .env:环境变量 > backend/.env > .env > 缺省(后端的 settings -# 读这两个文件,后一个优先)。不 source:.env 是给后端按 KEY=VALUE 读的,不是 shell(值里的 $、空格、反引号不该被执行) +# 这几项(AGENTLAB_DEV_PG_*,还有数据目录 AGENTLAB_DATA_DIR)和后端的配置一样认 .env:环境变量 > backend/.env > .env > 缺省 +# (后端的 settings 读这两个文件,后一个优先)。写法照后端的 dotenv:KEY=value、KEY = value、export KEY=value、引号、行尾 # 注释。 +# 不 source:.env 是给后端按 KEY=VALUE 读的,不是 shell(值里的 $、空格、反引号不该被执行) dotenv_value() { - local key="$1" file line val="" found="" + local key="$1" file line name val="" found="" for file in "$ROOT/.env" "$ROOT/backend/.env"; do [ -f "$file" ] || continue while IFS= read -r line || [ -n "$line" ]; do line="${line%$'\r'}" line="${line#"${line%%[![:space:]]*}"}" line="${line#export }" - case "$line" in - "$key="*) val="${line#*=}"; found=1 ;; - esac + case "$line" in *=*) ;; *) continue ;; esac + name="${line%%=*}" + name="${name%"${name##*[![:space:]]}"}" + [ "$name" = "$key" ] || continue + val="${line#*=}" + val="${val#"${val%%[![:space:]]*}"}" + found=1 done < "$file" done [ -n "$found" ] || return 1 @@ -96,18 +101,20 @@ dotenv_value() { esac printf '%s' "$val" } -dev_pg_setting() { +dotenv_setting() { local value value="$(printenv "$1" 2>/dev/null || true)" [ -n "$value" ] || value="$(dotenv_value "$1" || true)" printf '%s' "${value:-$2}" } if [ -n "$WITH_PG" ]; then - PG_CONTAINER="$(dev_pg_setting AGENTLAB_DEV_PG_CONTAINER agentlab-dev-postgres)" - PG_PORT="$(dev_pg_setting AGENTLAB_DEV_PG_PORT 55432)" - PG_VOLUME="$(dev_pg_setting AGENTLAB_DEV_PG_VOLUME agentlab-dev-pgdata)" - PG_IMAGE="$(dev_pg_setting AGENTLAB_DEV_PG_IMAGE postgres:16)" - DEV_DATA="${AGENTLAB_DATA_DIR:-$ROOT/data}" + PG_CONTAINER="$(dotenv_setting AGENTLAB_DEV_PG_CONTAINER agentlab-dev-postgres)" + PG_PORT="$(dotenv_setting AGENTLAB_DEV_PG_PORT 55432)" + PG_VOLUME="$(dotenv_setting AGENTLAB_DEV_PG_VOLUME agentlab-dev-pgdata)" + PG_IMAGE="$(dotenv_setting AGENTLAB_DEV_PG_IMAGE postgres:16)" + # 密码文件放在后端实际用的数据目录里:和后端一样读 AGENTLAB_DATA_DIR,相对路径按后端的工作目录(backend/)算 + DEV_DATA="$(dotenv_setting AGENTLAB_DATA_DIR "$ROOT/data")" + case "$DEV_DATA" in /*) ;; *) DEV_DATA="$ROOT/backend/$DEV_DATA" ;; esac case "$PG_PORT" in ''|*[!0-9]*) echo "AGENTLAB_DEV_PG_PORT 要是端口号:$PG_PORT"; exit 2 ;; esac command -v docker >/dev/null || { echo "--postgres 要用 docker,没找到它"; exit 1; } docker version --format '{{.Server.Version}}' >/dev/null 2>&1 || { echo "连不上 docker 服务,先把 Docker 启动起来"; exit 1; }