fix: resolve diagnostic paths from any cwd (#4736)

* fix: resolve diagnostic paths from any cwd

* test: cover relative diagnostic script paths
This commit is contained in:
MasonWight 2026-08-11 21:56:20 +08:00 committed by GitHub
parent df01102dfc
commit 6bb376abfd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 46 additions and 6 deletions

View File

@ -135,7 +135,7 @@ cd frontend && pnpm test # Unit tests
Rule of thumb: **root `make` = the full application**; **`backend/Makefile` and `frontend/`
(`pnpm`) = per-module work.**
Host-side pnpm consumers, including the root/frontend Makefiles and local diagnostic scripts, must run through `scripts/pnpm.py`. The runner preserves direct `pnpm`/`pnpm.cmd` priority, falls back to `corepack pnpm`, and is invoked from `frontend/` so Corepack honors the package-manager version pinned by that project.
Host-side pnpm consumers, including the root/frontend Makefiles and local diagnostic scripts, must run through `scripts/pnpm.py`. Diagnostic scripts resolve the runner and frontend directory to absolute paths before changing the child process working directory, so they remain independent of the caller's current directory. The runner preserves direct `pnpm`/`pnpm.cmd` priority, falls back to `corepack pnpm`, and is invoked from `frontend/` so Corepack honors the package-manager version pinned by that project.
## Where to Go Next

View File

@ -320,7 +320,7 @@ On Windows, run the local development flow from Git Bash. Native `cmd.exe` and P
make check # Verifies Node.js 22+, pnpm, uv, nginx
```
The local `make check`, `make install`, `make dev`, and `make start` entry points use a direct `pnpm`/`pnpm.cmd` executable when available and otherwise fall back to `corepack pnpm`. Corepack runs from `frontend/`, so it honors the `packageManager` version pinned in `frontend/package.json`; enabling a global pnpm shim is not required.
The local `make check`, `make install`, `make dev`, and `make start` entry points use a direct `pnpm`/`pnpm.cmd` executable when available and otherwise fall back to `corepack pnpm`. The shared runner and diagnostics resolve repository paths absolutely, so these checks work regardless of the caller's current directory. Corepack runs from `frontend/`, so it honors the `packageManager` version pinned in `frontend/package.json`; enabling a global pnpm shim is not required.
2. **Install dependencies**:
```bash

View File

@ -71,7 +71,20 @@ def test_find_pnpm_command_falls_back_to_corepack_cmd(monkeypatch):
def test_check_script_uses_shared_pnpm_runner():
check_script = CHECK_SCRIPT_PATH.read_text(encoding="utf-8")
assert 'Path(__file__).with_name("pnpm.py")' in check_script
assert 'Path(__file__).resolve().with_name("pnpm.py")' in check_script
def test_check_script_resolves_runner_paths_independently_of_cwd(monkeypatch):
# Reproduce invoking the entry point with a relative script path from the
# repository root. Before the fix, `__file__` stayed relative and the
# runner became `scripts/pnpm.py`, which later broke after cwd changed.
monkeypatch.chdir(REPO_ROOT)
check_script = _load_script(Path("scripts/check.py"), "deerflow_check_script_paths")
assert check_script.PNPM_SCRIPT_PATH == PNPM_SCRIPT_PATH
assert check_script.PNPM_SCRIPT_PATH.is_absolute()
assert check_script.FRONTEND_DIR == REPO_ROOT / "frontend"
assert check_script.FRONTEND_DIR.is_absolute()
def test_check_script_preserves_runner_failure_diagnostics(monkeypatch):

View File

@ -6,10 +6,25 @@ Run from repo root:
from __future__ import annotations
import importlib.util
import sys
from pathlib import Path
import doctor
REPO_ROOT = Path(__file__).resolve().parents[2]
def _load_script(path: Path, name: str):
assert path.exists(), f"{path} must exist"
spec = importlib.util.spec_from_file_location(name, path)
assert spec is not None
assert spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
# ---------------------------------------------------------------------------
# check_python
# ---------------------------------------------------------------------------
@ -28,6 +43,18 @@ class TestCheckPython:
class TestCheckPnpm:
def test_resolves_shared_runner_from_relative_script_path(self, monkeypatch):
# Load the script as `scripts/doctor.py`, as a user would from the
# repository root. The derived paths must not depend on that relative
# invocation path.
monkeypatch.chdir(REPO_ROOT)
relative_doctor = _load_script(Path("scripts/doctor.py"), "deerflow_doctor_relative")
assert relative_doctor.PNPM_SCRIPT_PATH == REPO_ROOT / "scripts" / "pnpm.py"
assert relative_doctor.PNPM_SCRIPT_PATH.is_absolute()
assert relative_doctor.FRONTEND_DIR == REPO_ROOT / "frontend"
assert relative_doctor.FRONTEND_DIR.is_absolute()
def test_uses_shared_runner_from_frontend(self, monkeypatch):
captured = {}

View File

@ -122,7 +122,7 @@ def test_official_entrypoints_route_pnpm_through_shared_runner():
assert 'DEERFLOW_PNPM_RUNNER="$REPO_ROOT/scripts/pnpm.py"' in serve_script
assert 'FRONTEND_CMD=\'"$DEERFLOW_PNPM_PYTHON" "$DEERFLOW_PNPM_RUNNER" run dev\'' in serve_script
assert '"\\$DEERFLOW_PNPM_RUNNER\\" run preview"' in serve_script
assert 'Path(__file__).with_name("pnpm.py")' in doctor_script
assert 'Path(__file__).resolve().with_name("pnpm.py")' in doctor_script
assert 'project_root / "scripts" / "pnpm.py"' in support_bundle_script

View File

@ -8,7 +8,7 @@ import subprocess
import sys
from pathlib import Path
PNPM_SCRIPT_PATH = Path(__file__).with_name("pnpm.py")
PNPM_SCRIPT_PATH = Path(__file__).resolve().with_name("pnpm.py")
FRONTEND_DIR = PNPM_SCRIPT_PATH.parent.parent / "frontend"
COREPACK_NOTICE = "Using pnpm via Corepack."

View File

@ -24,7 +24,7 @@ from typing import Literal
# ---------------------------------------------------------------------------
Status = Literal["ok", "warn", "fail", "skip"]
PNPM_SCRIPT_PATH = Path(__file__).with_name("pnpm.py")
PNPM_SCRIPT_PATH = Path(__file__).resolve().with_name("pnpm.py")
FRONTEND_DIR = PNPM_SCRIPT_PATH.parent.parent / "frontend"