From 967c7880fca00bbe97ebf50de3c8da65a2d97b54 Mon Sep 17 00:00:00 2001 From: Danny Date: Sun, 10 May 2026 12:43:42 +0200 Subject: [PATCH] chore: prefer BOT_TOKEN env var over secrets file Backward-compatible reorder: env var wins, then file. This lets multiple instances on the same host (prod + shipyard staging) each load a distinct token via systemd EnvironmentFile, instead of fighting over the single ~/.secrets/bigbiggerbiggestbot file. Also documents the new two-environment workflow in README. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 24 ++++++++++++++++++++---- bot.py | 16 +++++++++------- start.py | 25 ++++++++++++++----------- 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 4b97ce1..1faba7a 100644 --- a/README.md +++ b/README.md @@ -60,11 +60,27 @@ nix develop --command pytest tests/ -v ## Deployment -Runs as a systemd service. A timer pulls this repo periodically and -restarts the service when the remote has new commits — push to `main` -and the bot redeploys itself within ~15 minutes. +Two environments share one host (`sunken-ship`): -The SQLite database lives next to the code at `workouts.db` (gitignored). +- **Production** — `fitness-bot.service`, working dir `/home/danny/tg_fitness_bot`, + watches `origin/main`, served behind a stable URL via the VPS Caddy. +- **Shipyard staging** — `fitness-bot-shipyard.service`, working dir + `/home/danny/tg_fitness_bot_shipyard`, watches `origin/staging`, separate + bot token, ephemeral cloudflared URL each restart. + +Each has its own pull timer that fetches every ~15 minutes and restarts +the service when its branch has new commits. + +**Workflow:** + +``` +# 1. land changes on a working branch (or main locally) +git push origin :staging # → shipyard auto-deploys, test there +git push origin :main # → production auto-deploys +``` + +Each environment keeps its own `workouts.db` next to its code (gitignored), +so testing on shipyard never touches production data. ## Architecture diff --git a/bot.py b/bot.py index cd9ab46..d638082 100644 --- a/bot.py +++ b/bot.py @@ -27,22 +27,24 @@ logging.basicConfig( ) logger = logging.getLogger(__name__) -# Token resolution: secrets file → .env / environment variable +# Token resolution: BOT_TOKEN env var → secrets file +# Env var wins so multiple instances on the same host (e.g. prod + shipyard +# staging) can each point to a different token without sharing a secrets file. SECRETS_FILE = os.path.expanduser("~/.secrets/bigbiggerbiggestbot") def _load_token() -> str: - # 1. Try the secrets file + # 1. Env var (set by systemd EnvironmentFile in multi-instance setups) + token = os.environ.get("BOT_TOKEN", "").strip() + if token: + return token + # 2. Default secrets file if os.path.isfile(SECRETS_FILE): token = open(SECRETS_FILE).read().strip() if token: return token - # 2. Fall back to env var (set via .env or shell) - token = os.environ.get("BOT_TOKEN", "").strip() - if token: - return token raise RuntimeError( - f"No bot token found. Put it in {SECRETS_FILE} or set BOT_TOKEN env var." + f"No bot token found. Set BOT_TOKEN env var or put it in {SECRETS_FILE}." ) diff --git a/start.py b/start.py index 2710741..df1537d 100644 --- a/start.py +++ b/start.py @@ -21,15 +21,25 @@ SECRETS_FILE = pathlib.Path.home() / ".secrets" / "bigbiggerbiggestbot" def load_token() -> str: - """Load bot token: secrets file → .env → BOT_TOKEN env var.""" - # 1. Secrets file (same path as bot.py uses) + """Load bot token: BOT_TOKEN env → secrets file → .env in cwd. + + Env var wins so multiple instances on the same host (prod + shipyard) + can each get a distinct token via systemd EnvironmentFile. + """ + # 1. Already in environment (systemd EnvironmentFile sets this for staging) + token = os.environ.get("BOT_TOKEN", "").strip() + if token: + print(" Token loaded from BOT_TOKEN env var") + return token + + # 2. Secrets file (same path as bot.py uses) if SECRETS_FILE.is_file(): token = SECRETS_FILE.read_text().strip() if token: print(f" Token loaded from {SECRETS_FILE}") return token - # 2. .env in working directory + # 3. .env in working directory env_file = pathlib.Path.cwd() / ".env" if env_file.exists(): for line in env_file.read_text().splitlines(): @@ -40,15 +50,8 @@ def load_token() -> str: print(f" Token loaded from {env_file}") return token - # 3. Already in environment - token = os.environ.get("BOT_TOKEN", "").strip() - if token: - print(" Token loaded from BOT_TOKEN env var") - return token - print("\n No bot token found!") - print(f" Put it in {SECRETS_FILE}") - print(" Or create a .env file with: BOT_TOKEN=your-token\n") + print(f" Set BOT_TOKEN env var, or put it in {SECRETS_FILE}, or in a .env file.\n") sys.exit(1)