bastebin/setup.py

154 lines
4.4 KiB
Python

#!/usr/bin/env python3
"""
setup.py — First-time setup for Bastebin.
What this does:
1. Checks that a supported Python version is available.
2. Creates a virtual environment at .venv/ (if absent).
3. Installs all dependencies from requirements.txt into the venv.
4. Initialises the SQLite database (creates the pastes table).
5. Warns if config.json contains the default secret key.
Usage:
python setup.py
"""
import os
import subprocess
import sys
MIN_PYTHON = (3, 11)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
VENV_DIR = os.path.join(BASE_DIR, '.venv')
REQS_FILE = os.path.join(BASE_DIR, 'requirements.txt')
CONFIG = os.path.join(BASE_DIR, 'config.json')
# ── Helpers ───────────────────────────────────────────────────────────────────
def _step(msg: str) -> None:
print(f'\n {msg}')
def _ok(msg: str = 'done') -> None:
print(f'{msg}')
def _warn(msg: str) -> None:
print(f'{msg}')
def _fail(msg: str) -> None:
print(f'\n{msg}')
sys.exit(1)
def _venv_python() -> str:
return os.path.join(VENV_DIR, 'bin', 'python')
def _venv_pip() -> str:
return os.path.join(VENV_DIR, 'bin', 'pip')
def _run(*args: str, capture: bool = False) -> subprocess.CompletedProcess:
return subprocess.run(
args,
check=True,
capture_output=capture,
text=True,
)
# ── Steps ─────────────────────────────────────────────────────────────────────
def check_python() -> None:
_step('Checking Python version...')
if sys.version_info < MIN_PYTHON:
_fail(
f'Python {MIN_PYTHON[0]}.{MIN_PYTHON[1]}+ is required '
f'(found {sys.version.split()[0]}).'
)
_ok(f'Python {sys.version.split()[0]}')
def create_venv() -> None:
_step('Setting up virtual environment...')
if os.path.isfile(_venv_python()):
_ok('.venv already exists — skipping creation')
return
_run(sys.executable, '-m', 'venv', VENV_DIR)
_ok(f'Created {VENV_DIR}')
def install_dependencies() -> None:
_step('Installing dependencies...')
if not os.path.isfile(REQS_FILE):
_fail(f'requirements.txt not found at {REQS_FILE}')
_run(_venv_pip(), 'install', '--quiet', '--upgrade', 'pip')
_run(_venv_pip(), 'install', '--quiet', '-r', REQS_FILE)
_ok('All packages installed')
def initialise_database() -> None:
_step('Initialising database...')
# Import app inside the venv's Python so the correct packages are used.
result = _run(
_venv_python(),
'-c',
'import sys; sys.path.insert(0, "."); from app import init_db; init_db(); print("ok")',
capture=True,
)
if 'ok' in result.stdout:
_ok('Database ready')
else:
_warn('Database initialisation produced unexpected output — check manually.')
def check_secret_key() -> None:
_step('Checking configuration...')
try:
import json
with open(CONFIG, encoding='utf-8') as f:
cfg = json.load(f)
key = cfg.get('server', {}).get('secret_key', '')
if key in ('', 'change-this-to-a-long-random-secret', 'change-me'):
_warn(
'config.json still has the default secret_key. '
'Set a long, random value before deploying to production.'
)
else:
_ok('Secret key looks good')
except (FileNotFoundError, Exception) as e:
_warn(f'Could not read config.json: {e}')
# ── Entry point ───────────────────────────────────────────────────────────────
def main() -> None:
print('' * 50)
print(' Bastebin setup')
print('' * 50)
check_python()
create_venv()
install_dependencies()
initialise_database()
check_secret_key()
print()
print('' * 50)
print(' Setup complete.')
print()
print(' Start the server:')
print(' python production.py start')
print()
print(' Stop the server:')
print(' python production.py stop')
print('' * 50)
print()
if __name__ == '__main__':
main()