|
|
@@ -181,12 +181,16 @@ def _parse_github_remote(url: str) -> tuple[str, str] | None:
|
|
|
return (parts[0], parts[1])
|
|
|
|
|
|
|
|
|
-async def _origin_points_at_repo(git_path: str, git_config: list[str], base_dir, expected_repo: str) -> bool:
|
|
|
+async def _origin_points_at_repo(git_path: str, git_config: list[str], app_dir, expected_repo: str) -> bool:
|
|
|
"""Return True iff the working tree's `origin` already resolves to
|
|
|
`<owner>/<repo>` matching `expected_repo` (e.g. "maziggy/bambuddy"),
|
|
|
regardless of whether it's the SSH or HTTPS form. Used to skip the
|
|
|
`git remote set-url origin https://...` rewrite when the developer's
|
|
|
- SSH origin is already correct — see `_perform_update` for context."""
|
|
|
+ SSH origin is already correct — see `_perform_update` for context.
|
|
|
+
|
|
|
+ ``app_dir`` is the working tree (where ``.git`` lives), not the data
|
|
|
+ dir — see #1715 for the separate-mount layout that proved why this
|
|
|
+ must NOT be ``base_dir``."""
|
|
|
try:
|
|
|
process = await asyncio.create_subprocess_exec(
|
|
|
git_path,
|
|
|
@@ -194,7 +198,7 @@ async def _origin_points_at_repo(git_path: str, git_config: list[str], base_dir,
|
|
|
"remote",
|
|
|
"get-url",
|
|
|
"origin",
|
|
|
- cwd=str(base_dir),
|
|
|
+ cwd=str(app_dir),
|
|
|
stdout=asyncio.subprocess.PIPE,
|
|
|
stderr=asyncio.subprocess.PIPE,
|
|
|
)
|
|
|
@@ -555,7 +559,16 @@ async def _perform_update(target_ref: str):
|
|
|
global _update_status
|
|
|
|
|
|
try:
|
|
|
- base_dir = settings.base_dir
|
|
|
+ # Every git step runs against the working tree (app_dir), NOT base_dir.
|
|
|
+ # On a standard install with DATA_DIR=INSTALL_PATH/data, git happens
|
|
|
+ # to walk up from a subdirectory of the repo to find .git so cwd=base_dir
|
|
|
+ # used to silently work — but only by accident. On a native install with
|
|
|
+ # DATA_DIR mounted at an unrelated path (e.g. /srv/bambuddy/data while
|
|
|
+ # the install is /opt/bambuddy — see #1715), git can't walk up and every
|
|
|
+ # operation fails with "not a git repository". safe.directory has the
|
|
|
+ # same requirement: it must equal the repo root git discovers, not the
|
|
|
+ # data dir, or every call returns "fatal: detected dubious ownership."
|
|
|
+ app_dir = settings.app_dir
|
|
|
|
|
|
# Find git executable (may not be in PATH when running as systemd service)
|
|
|
git_path = _find_executable("git")
|
|
|
@@ -570,8 +583,9 @@ async def _perform_update(target_ref: str):
|
|
|
|
|
|
logger.info("Using git at: %s", git_path)
|
|
|
|
|
|
- # Git config to avoid safe.directory issues
|
|
|
- git_config = ["-c", f"safe.directory={base_dir}"]
|
|
|
+ # Git config to avoid safe.directory issues — must point at the working
|
|
|
+ # tree (where .git lives), see app_dir comment above.
|
|
|
+ git_config = ["-c", f"safe.directory={app_dir}"]
|
|
|
|
|
|
_update_status = {
|
|
|
"status": "downloading",
|
|
|
@@ -593,7 +607,7 @@ async def _perform_update(target_ref: str):
|
|
|
# correct repo are preserved; only missing / wrong / corrupted
|
|
|
# origins get reset to HTTPS.
|
|
|
https_url = f"https://github.com/{GITHUB_REPO}.git"
|
|
|
- if not await _origin_points_at_repo(git_path, git_config, base_dir, GITHUB_REPO):
|
|
|
+ if not await _origin_points_at_repo(git_path, git_config, app_dir, GITHUB_REPO):
|
|
|
process = await asyncio.create_subprocess_exec(
|
|
|
git_path,
|
|
|
*git_config,
|
|
|
@@ -601,7 +615,7 @@ async def _perform_update(target_ref: str):
|
|
|
"set-url",
|
|
|
"origin",
|
|
|
https_url,
|
|
|
- cwd=str(base_dir),
|
|
|
+ cwd=str(app_dir),
|
|
|
stdout=asyncio.subprocess.PIPE,
|
|
|
stderr=asyncio.subprocess.PIPE,
|
|
|
)
|
|
|
@@ -635,7 +649,7 @@ async def _perform_update(target_ref: str):
|
|
|
"--tags",
|
|
|
"--force",
|
|
|
"origin",
|
|
|
- cwd=str(base_dir),
|
|
|
+ cwd=str(app_dir),
|
|
|
stdout=asyncio.subprocess.PIPE,
|
|
|
stderr=asyncio.subprocess.PIPE,
|
|
|
)
|
|
|
@@ -671,7 +685,7 @@ async def _perform_update(target_ref: str):
|
|
|
"reset",
|
|
|
"--hard",
|
|
|
target_ref,
|
|
|
- cwd=str(base_dir),
|
|
|
+ cwd=str(app_dir),
|
|
|
stdout=asyncio.subprocess.PIPE,
|
|
|
stderr=asyncio.subprocess.PIPE,
|
|
|
)
|
|
|
@@ -696,12 +710,9 @@ async def _perform_update(target_ref: str):
|
|
|
}
|
|
|
|
|
|
# Install Python dependencies — must run from the source-code directory
|
|
|
- # (where requirements.txt lives), not the data dir. On native installs
|
|
|
- # systemd sets DATA_DIR=INSTALL_PATH/data, so `base_dir` is the data dir,
|
|
|
- # not the working tree. `git reset` above worked from base_dir because
|
|
|
- # git walks up looking for .git, but `pip install -r requirements.txt`
|
|
|
- # needs the file in cwd literally.
|
|
|
- app_dir = settings.app_dir
|
|
|
+ # (where requirements.txt lives). app_dir is already resolved at the top
|
|
|
+ # of this function; see the comment there for why every step uses it
|
|
|
+ # instead of base_dir.
|
|
|
process = await asyncio.create_subprocess_exec(
|
|
|
sys.executable,
|
|
|
"-m",
|