File size: 3,534 Bytes
bfd6783
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#!/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 "<generate a random string>")"

cat <<EOF

✅ Uploaded to https://huggingface.co/spaces/${SPACE_ID}

Still to do (only you can do these):

  1. Space Settings > 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