#!/usr/bin/env bash # # Push coco-finbot to a Hugging Face Space. # # Uses `hf upload` rather than git, deliberately: this directory sits inside the # larger UniPhy repo, so `git push` here would try to push that entire tree -- # unrelated repos and any secrets they hold -- to a public-by-default host. # # Run `hf auth login` first. Secrets are NOT set here; put them in the Space # Settings > Secrets UI so they never land in your shell history. # # Usage: # ./deploy.sh # deploy under your own account # ./deploy.sh Uniphy # deploy under an org you belong to # ./deploy.sh "" my-bot my-data # custom space / dataset names # ./deploy.sh --dry-run # show what would happen, create nothing set -euo pipefail DRY_RUN=0 if [[ "${1:-}" == "--dry-run" ]]; then DRY_RUN=1 shift fi OWNER="${1:-}" SPACE_NAME="${2:-coco-finbot}" DATA_NAME="${3:-finbot-data}" HF="./.venv/bin/hf" PY="./.venv/bin/python" [[ -x "$HF" ]] || HF="hf" [[ -x "$PY" ]] || PY="python3" echo "▸ Checking authentication" # `hf auth whoami` prints ANSI-coloured labelled lines ("user: name") and exits # 0 even when logged out, so ask the API for a clean value instead of scraping. ACCOUNT="$( "$PY" - <<'PY' 2>/dev/null || true try: from huggingface_hub import HfApi print(HfApi().whoami()["name"]) except Exception: pass PY )" if [[ -z "$ACCOUNT" ]]; then echo " Not logged in. Run: $HF auth login" >&2 exit 1 fi echo " logged in as: $ACCOUNT" OWNER="${OWNER:-$ACCOUNT}" SPACE_ID="${OWNER}/${SPACE_NAME}" DATA_ID="${OWNER}/${DATA_NAME}" # Fail early with a readable message rather than a validation traceback. if [[ ! "$OWNER" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]]; then echo " Invalid owner name: '$OWNER'" >&2 exit 1 fi echo "▸ Target Space: $SPACE_ID (docker, private)" echo "▸ Target dataset: $DATA_ID (private, durable ledger)" if [[ "$DRY_RUN" == "1" ]]; then echo echo "(dry run — nothing created or uploaded)" exit 0 fi echo "▸ Creating Space" "$HF" repo create "$SPACE_ID" --repo-type space --space_sdk docker --private --exist-ok echo "▸ Creating dataset" "$HF" repo create "$DATA_ID" --repo-type dataset --private --exist-ok echo "▸ Uploading source" # Everything the container does not need, or that must never leave this machine. "$HF" upload "$SPACE_ID" . . \ --repo-type space \ --commit-message "Deploy coco-finbot" \ --exclude \ ".venv/*" \ "**/__pycache__/*" \ ".pytest_cache/*" \ ".ruff_cache/*" \ "data/*" \ "*.db" \ ".env" \ ".git/*" SECRET="$("$PY" -c "import secrets; print(secrets.token_urlsafe(32))" 2>/dev/null || echo "")" cat < Secrets, add: TELEGRAM_BOT_TOKEN from @BotFather TELEGRAM_WEBHOOK_SECRET ${SECRET} ALLOWED_TELEGRAM_USER_IDS your numeric Telegram id HF_DATASET_REPO ${DATA_ID} HF_TOKEN a write token with Inference Providers access 2. Confirm the Space is Private (Settings > Change visibility). 3. Restart the Space. It registers its own Telegram webhook on boot. 4. Message your bot. If you don't know your Telegram id, send it anything -- it replies with the id to paste into ALLOWED_TELEGRAM_USER_IDS, then restart the Space once more. 5. Send /status and confirm storage says "mirroring", not "local only". EOF