108 lines
3.0 KiB
Python
108 lines
3.0 KiB
Python
"""Creación automática de config/ y carpetas de trabajo."""
|
|
|
|
import shutil
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from ..constants import (
|
|
APP_DIR,
|
|
BUNDLE_DIR,
|
|
BUNDLED_SOURCE_7ZIP,
|
|
BUNDLED_SOURCE_ODBC,
|
|
CONFIG_DIR,
|
|
DATA_DIR,
|
|
DIR_ENTRADA,
|
|
DIR_FALLADOS,
|
|
DIR_PROCESADOS,
|
|
DIR_TEMP,
|
|
ENV_PATH,
|
|
IS_WINDOWS,
|
|
LOGS_DIR,
|
|
ODBC_DIR,
|
|
SEVEN_ZIP_DIR,
|
|
)
|
|
from ..db.database import DatabaseManager
|
|
from .env_loader import render_env_template
|
|
|
|
|
|
def _copy_tree_if_missing(src: Path, dest: Path) -> None:
|
|
if not src.is_dir():
|
|
return
|
|
# dest puede existir pero VACÍO: ensure_runtime_layout crea ODBC_DIR/SEVEN_ZIP_DIR
|
|
# antes de llamar aquí. Solo saltar si ya tiene contenido (evita re-copiar en cada
|
|
# arranque). Antes se saltaba por dest.exists(), dejando 7-Zip/ODBC sin desplegar.
|
|
if dest.exists() and any(dest.iterdir()):
|
|
return
|
|
dest.mkdir(parents=True, exist_ok=True)
|
|
shutil.copytree(src, dest, dirs_exist_ok=True)
|
|
|
|
|
|
def _ensure_seven_zip_executable() -> None:
|
|
"""
|
|
Asegura el bit de ejecución del 7-Zip embebido en Linux/macOS. shutil.copytree
|
|
suele preservar los permisos, pero esto es una salvaguarda barata: si el binario
|
|
quedó sin 'x', la extracción fallaría con PermissionError.
|
|
"""
|
|
if IS_WINDOWS:
|
|
return
|
|
for exe_name in ("7zz", "7za", "7z"):
|
|
exe_path = SEVEN_ZIP_DIR / exe_name
|
|
if exe_path.is_file():
|
|
try:
|
|
exe_path.chmod(0o755)
|
|
except OSError:
|
|
pass
|
|
|
|
|
|
def _write_env_if_missing() -> None:
|
|
if ENV_PATH.exists():
|
|
return
|
|
template_path = BUNDLE_DIR / "packaging" / "templates" / "env.default"
|
|
if not template_path.is_file():
|
|
template_path = APP_DIR / "packaging" / "templates" / "env.default"
|
|
if template_path.is_file():
|
|
content = template_path.read_text(encoding="utf-8")
|
|
content = content.replace("{APP_DIR}", str(APP_DIR))
|
|
else:
|
|
content = render_env_template(APP_DIR)
|
|
ENV_PATH.write_text(content, encoding="utf-8")
|
|
try:
|
|
ENV_PATH.chmod(0o600)
|
|
except OSError:
|
|
pass
|
|
|
|
|
|
def ensure_runtime_layout() -> None:
|
|
"""
|
|
Idempotente: crea config/, carpetas de trabajo y .env si no existen.
|
|
No sobrescribe .env ni app.db existentes.
|
|
"""
|
|
for directory in (
|
|
CONFIG_DIR,
|
|
DATA_DIR,
|
|
LOGS_DIR,
|
|
ODBC_DIR,
|
|
SEVEN_ZIP_DIR,
|
|
DIR_ENTRADA,
|
|
DIR_PROCESADOS,
|
|
DIR_FALLADOS,
|
|
DIR_TEMP,
|
|
):
|
|
directory.mkdir(parents=True, exist_ok=True)
|
|
|
|
_write_env_if_missing()
|
|
|
|
_copy_tree_if_missing(BUNDLED_SOURCE_7ZIP, SEVEN_ZIP_DIR)
|
|
_copy_tree_if_missing(BUNDLED_SOURCE_ODBC, ODBC_DIR)
|
|
_ensure_seven_zip_executable()
|
|
|
|
from ..constants import DB_PATH
|
|
|
|
if not DB_PATH.exists():
|
|
DatabaseManager(DB_PATH)
|
|
|
|
if getattr(sys, "frozen", False):
|
|
marker = CONFIG_DIR / ".bootstrap_ok"
|
|
if not marker.exists():
|
|
marker.write_text("ok", encoding="utf-8")
|