simplefe

Log | Files | Refs

install.py (3896B)


      1 #!/usr/bin/env python3
      2 import getpass
      3 import os
      4 import secrets
      5 import shutil
      6 import subprocess
      7 import sys
      8 import tempfile
      9 import venv
     10 from pathlib import Path
     11 
     12 SOURCE_ROOT = Path(__file__).resolve().parent
     13 DEPLOY_ROOT = Path("/opt/simplefe")
     14 VENV_DIR = DEPLOY_ROOT / ".venv"
     15 VENV_PYTHON = VENV_DIR / "bin" / "python"
     16 SIMPLEFE_TOKEN_KEY = "SIMPLEFE_TOKEN"
     17 
     18 SYNC_EXCLUDES = [
     19     ".venv",
     20     ".git",
     21     ".env",
     22     "__pycache__",
     23     ".pytest_cache",
     24     "*.egg-info",
     25 ]
     26 
     27 
     28 def sync_source():
     29     if SOURCE_ROOT == DEPLOY_ROOT:
     30         return
     31     print(f"Syncing source to {DEPLOY_ROOT} (requires sudo)...")
     32     subprocess.run(["sudo", "mkdir", "-p", str(DEPLOY_ROOT)], check=True)
     33     user = getpass.getuser()
     34     subprocess.run(
     35         ["sudo", "chown", "-R", f"{user}:{user}", str(DEPLOY_ROOT)], check=True
     36     )
     37     shutil.copytree(
     38         SOURCE_ROOT,
     39         DEPLOY_ROOT,
     40         dirs_exist_ok=True,
     41         ignore=shutil.ignore_patterns(*SYNC_EXCLUDES),
     42     )
     43 
     44 
     45 def create_venv():
     46     if VENV_DIR.exists():
     47         print(f"Virtual environment already exists at {VENV_DIR}, skipping.")
     48         return
     49     print(f"Creating virtual environment at {VENV_DIR}...")
     50     venv.create(VENV_DIR, with_pip=True)
     51 
     52 
     53 def install_package():
     54     print("Installing simplefe and its dependencies...")
     55     subprocess.run(
     56         [str(VENV_PYTHON), "-m", "pip", "install", "-e", str(DEPLOY_ROOT)],
     57         check=True,
     58     )
     59 
     60 
     61 def reexec_into_venv():
     62     if Path(sys.prefix) == VENV_DIR:
     63         return
     64     os.execv(str(VENV_PYTHON), [str(VENV_PYTHON), __file__, *sys.argv[1:]])
     65 
     66 
     67 def write_env_file():
     68     env_path = DEPLOY_ROOT / ".env"
     69     if env_path.exists():
     70         if f"{SIMPLEFE_TOKEN_KEY}=" in env_path.read_text():
     71             print(f"{env_path} already has a {SIMPLEFE_TOKEN_KEY}, keeping it as-is.")
     72             return
     73         token = secrets.token_urlsafe(32)
     74         with open(env_path, "a") as f:
     75             f.write(f"{SIMPLEFE_TOKEN_KEY}={token}\n")
     76         print(f"Added a freshly generated {SIMPLEFE_TOKEN_KEY} to {env_path}.")
     77         return
     78 
     79     token = secrets.token_urlsafe(32)
     80     with open(env_path, "w") as f:
     81         f.write(f"{SIMPLEFE_TOKEN_KEY}={token}\n")
     82 
     83     print(f"Wrote {env_path} with a freshly generated {SIMPLEFE_TOKEN_KEY}.")
     84     print(
     85         "Once the service is running, visit /setup in your browser to add your "
     86         "Jellyfin and Pushover credentials. Any ENCODE_*/DRIVE_PATH/OUTPUT_DIR/"
     87         "OUTPUT_GROUP overrides still need to be added to .env by hand (see "
     88         "settings.py for the full list and defaults)."
     89     )
     90 
     91 
     92 SYSTEMD_UNIT_PATH = Path("/etc/systemd/system/simplefe.service")
     93 
     94 UNIT_TEMPLATE = """\
     95 [Unit]
     96 Description=simplefe
     97 After=network.target
     98 
     99 [Service]
    100 Type=simple
    101 User={user}
    102 WorkingDirectory={project_root}
    103 ExecStart={waitress} --listen=0.0.0.0:8000 --call simplefe.app:create_app
    104 Restart=on-failure
    105 
    106 [Install]
    107 WantedBy=multi-user.target
    108 """
    109 
    110 
    111 def install_systemd_unit():
    112     waitress = VENV_DIR / "bin" / "waitress-serve"
    113     unit_content = UNIT_TEMPLATE.format(
    114         user=getpass.getuser(),
    115         project_root=DEPLOY_ROOT,
    116         waitress=waitress,
    117     )
    118 
    119     with tempfile.NamedTemporaryFile("w", suffix=".service", delete=False) as f:
    120         f.write(unit_content)
    121         tmp_path = f.name
    122 
    123     print("Installing systemd service (requires sudo)...")
    124     subprocess.run(["sudo", "cp", tmp_path, str(SYSTEMD_UNIT_PATH)], check=True)
    125     Path(tmp_path).unlink()
    126     subprocess.run(["sudo", "systemctl", "daemon-reload"], check=True)
    127     subprocess.run(["sudo", "systemctl", "enable", "simplefe"], check=True)
    128     subprocess.run(["sudo", "systemctl", "restart", "simplefe"], check=True)
    129     print("simplefe service installed and started.")
    130 
    131 
    132 def main():
    133     sync_source()
    134     create_venv()
    135     install_package()
    136     reexec_into_venv()
    137 
    138     write_env_file()
    139     install_systemd_unit()
    140 
    141 
    142 if __name__ == "__main__":
    143     main()