mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-03 04:31:28 +02:00
Native-install upgrade via the in-app Apply Update button got the new
code in via `git reset --hard origin/main` but then logged
ERROR: Could not open requirements file:
[Errno 2] No such file or directory: 'requirements.txt'
and continued. The new deps never installed, leaving the user with
new code but stale dependencies — surfaces as cryptic import errors
on the next restart.
Root cause: `pip install -r requirements.txt` ran with
`cwd=settings.base_dir`. On a native install, systemd sets
DATA_DIR=$INSTALL_PATH/data so base_dir resolves to the data dir
(e.g. /opt/bambuddy/data), not the source tree. Pip doesn't walk up
looking for the requirements file the way git walks up looking for
.git, so it fails. Same bug affected the optional npm step
(`frontend_dir = base_dir / "frontend"` doesn't exist).
Fix: introduce `settings.app_dir` pointing at the source-tree root
(distinct from `base_dir` only on native installs) and run pip +
npm with `cwd=settings.app_dir`. Git ops keep using `base_dir`
because they already work (git walks up).
Docker users were unaffected — Docker doesn't use the in-app updater
(image pull replaces it).
Regression test in test_updates_api.py mocks every subprocess in
_perform_update, captures their cwd, and asserts the pip step runs
in app_dir and that requirements.txt actually exists there. Any
future refactor that re-introduces cwd=base_dir for the pip step
fails CI before another user trips over it.
104 lines
4.1 KiB
Python
104 lines
4.1 KiB
Python
import logging
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from pydantic_settings import BaseSettings
|
|
|
|
# Application version - single source of truth
|
|
APP_VERSION = "0.2.4b1"
|
|
GITHUB_REPO = "maziggy/bambuddy"
|
|
BUG_REPORT_RELAY_URL = os.environ.get("BUG_REPORT_RELAY_URL", "https://bambuddy.cool/api/bug-report")
|
|
|
|
# App directory - where the application is installed (for static files)
|
|
_app_dir = Path(__file__).resolve().parent.parent.parent.parent
|
|
|
|
# Data directory - for persistent data (database, archives)
|
|
# Use DATA_DIR env var if set (Docker), otherwise use project root (local dev)
|
|
_data_dir_env = os.environ.get("DATA_DIR")
|
|
_data_dir = Path(_data_dir_env) if _data_dir_env else _app_dir
|
|
|
|
# Plate calibration directory - special handling to maintain backwards compatibility
|
|
# Docker: DATA_DIR/plate_calibration (e.g., /data/plate_calibration)
|
|
# Local dev: project_root/data/plate_calibration (original location)
|
|
_plate_cal_dir = Path(_data_dir_env) / "plate_calibration" if _data_dir_env else _app_dir / "data" / "plate_calibration"
|
|
|
|
# Log directory - use LOG_DIR env var if set, otherwise use app_dir/logs
|
|
_log_dir_env = os.environ.get("LOG_DIR")
|
|
_log_dir = Path(_log_dir_env) if _log_dir_env else _app_dir / "logs"
|
|
|
|
|
|
def _migrate_database() -> Path:
|
|
"""Migrate database from old name to new name if needed."""
|
|
old_db = _data_dir / "bambutrack.db"
|
|
new_db = _data_dir / "bambuddy.db"
|
|
|
|
# If old database exists and new one doesn't, rename it
|
|
if old_db.exists() and not new_db.exists():
|
|
try:
|
|
old_db.rename(new_db)
|
|
logging.info("Migrated database: %s -> %s", old_db, new_db)
|
|
except Exception as e:
|
|
logging.warning("Could not migrate database: %s. Using old location.", e)
|
|
return old_db
|
|
|
|
# If old database exists (and new one now exists too), it was migrated
|
|
# If only new exists, use new
|
|
# If neither exists, use new (will be created)
|
|
return new_db if new_db.exists() or not old_db.exists() else old_db
|
|
|
|
|
|
# External DATABASE_URL takes priority (PostgreSQL support)
|
|
_external_db_url = os.environ.get("DATABASE_URL")
|
|
|
|
# Determine database path (handles migration) — only used for SQLite
|
|
_db_path = _migrate_database() if not _external_db_url else None
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
app_name: str = "Bambuddy"
|
|
debug: bool = False # Default to production mode
|
|
|
|
# Paths
|
|
base_dir: Path = _data_dir # For backwards compatibility
|
|
# `app_dir` is where the source code is checked out — distinct from `base_dir`
|
|
# on native installs where DATA_DIR is set to a sibling like INSTALL_PATH/data.
|
|
# Use this when you need the working tree (requirements.txt, frontend/, etc.)
|
|
# rather than the data dir. On Docker / local dev where DATA_DIR is unset,
|
|
# app_dir == base_dir.
|
|
app_dir: Path = _app_dir
|
|
archive_dir: Path = _data_dir / "archive"
|
|
plate_calibration_dir: Path = _plate_cal_dir # Plate detection references
|
|
static_dir: Path = _app_dir / "static" # Static files are part of app, not data
|
|
log_dir: Path = _log_dir
|
|
database_url: str = _external_db_url or f"sqlite+aiosqlite:///{_db_path}"
|
|
|
|
# Logging
|
|
log_level: str = "INFO" # Override with LOG_LEVEL env var or DEBUG=true
|
|
log_to_file: bool = True # Set to false to disable file logging
|
|
|
|
# API
|
|
api_prefix: str = "/api/v1"
|
|
|
|
# Slicer API sidecars. Defaults match the docker-compose.yml ports in the
|
|
# orca-slicer-api fork (https://github.com/maziggy/orca-slicer-api):
|
|
# OrcaSlicer → port 3003 (default profile)
|
|
# BambuStudio → port 3001 (built locally via Dockerfile.bambu-studio)
|
|
# The slice route picks which one based on the user's preferred_slicer
|
|
# setting.
|
|
slicer_api_url: str = "http://localhost:3003"
|
|
bambu_studio_api_url: str = "http://localhost:3001"
|
|
|
|
class Config:
|
|
env_file = ".env"
|
|
env_file_encoding = "utf-8"
|
|
|
|
|
|
settings = Settings()
|
|
|
|
# Ensure directories exist
|
|
settings.archive_dir.mkdir(parents=True, exist_ok=True)
|
|
settings.plate_calibration_dir.mkdir(parents=True, exist_ok=True)
|
|
settings.static_dir.mkdir(exist_ok=True)
|
|
if settings.log_to_file:
|
|
settings.log_dir.mkdir(exist_ok=True)
|