154 lines
4.4 KiB
Python
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()
|