File size: 7,231 Bytes
c6302b1
 
 
a009492
 
 
 
c6302b1
 
a009492
 
 
 
 
 
c6302b1
 
 
 
a009492
feec173
c6302b1
feec173
 
c6302b1
 
 
feec173
 
c6302b1
feec173
a009492
feec173
c6302b1
feec173
c6302b1
 
feec173
 
 
 
a009492
 
 
 
feec173
 
 
c6302b1
a009492
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
feec173
 
a009492
feec173
 
 
 
 
c6302b1
 
feec173
a009492
feec173
 
 
 
 
 
 
c6302b1
 
 
 
feec173
c6302b1
 
feec173
a009492
feec173
 
 
 
 
 
 
c6302b1
feec173
c6302b1
 
 
 
 
a009492
c6302b1
 
feec173
 
 
 
 
 
 
 
a009492
 
 
 
 
 
 
 
 
 
 
 
 
c6302b1
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#!/usr/bin/env bash
# Deploy this repo to a Hugging Face Docker Space.
#
# Requires an authenticated Hugging Face CLI session that can create Spaces and
# Buckets. XTAP_STORAGE_TOKEN must have read/write access to exactly the raw and
# index Buckets. Supplying it authorizes this script to install it as the Space
# HF_TOKEN secret.
#
# Usage:
#   PINNED_IMPORT_REPORT=/safe/path/report.json XTAP_STORAGE_TOKEN=... \
#     scripts/deploy-space.sh <namespace>
#   SPACE_REPO=<ns>/<name> RAW_BUCKET=<ns>/<name> INDEX_BUCKET=<ns>/<name> \
#     PINNED_IMPORT_REPORT=/safe/path/report.json XTAP_STORAGE_TOKEN=... \
#     scripts/deploy-space.sh
# Set ALLOW_EMPTY_POOL=1 only for a confirmed new pool with no legacy data.
set -euo pipefail

NAMESPACE="${1:-${NAMESPACE:-}}"
SPACE_REPO="${SPACE_REPO:-${NAMESPACE:?usage: deploy-space.sh <namespace>}/xtap-pool}"
RAW_BUCKET="${RAW_BUCKET:-${NAMESPACE}/xtap-pool-data}"
INDEX_BUCKET="${INDEX_BUCKET:-${NAMESPACE}/xtap-pool-bucket}"
ALLOWED_USERS="${ALLOWED_USERS:-osolmaz}"
LLM_MODEL="${LLM_MODEL:-zai-org/GLM-5.2:fireworks-ai}"
TAXONOMY_VERSION="${TAXONOMY_VERSION:-1}"

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
STAGE="$(mktemp -d)"
INDEX_WORK="$(mktemp -d)"
trap 'rm -rf "$STAGE" "$INDEX_WORK"' EXIT

echo "==> Creating storage and Space resources (idempotent)"
hf buckets create "$RAW_BUCKET" --private --exist-ok
hf buckets create "$INDEX_BUCKET" --private --exist-ok
# The Space itself is public: a private Space would put HF's repo-access gate
# in front of the app. Anonymous visitors still see only the sign-in page.
hf repos create "$SPACE_REPO" --repo-type space --space-sdk docker 2>/dev/null || true

if [[ -z "${XTAP_STORAGE_TOKEN:-}" ]]; then
  cat >&2 <<EOF
XTAP_STORAGE_TOKEN is required before deployment.
Create a fine-grained token with read/write access to exactly:
  Bucket: $RAW_BUCKET
  Bucket: $INDEX_BUCKET
Rerun with XTAP_STORAGE_TOKEN set. The script will bootstrap the durable index
and install the same value as the Space HF_TOKEN.
EOF
  exit 2
fi

if [[ "${ALLOW_EMPTY_POOL:-0}" != "1" ]]; then
  if [[ -z "${PINNED_IMPORT_REPORT:-}" || ! -f "$PINNED_IMPORT_REPORT" ]]; then
    cat >&2 <<EOF
PINNED_IMPORT_REPORT must name the verified report from storage:import.
The deployment will not bootstrap an empty replacement over a legacy pool.
For a confirmed new pool with no legacy data, set ALLOW_EMPTY_POOL=1 explicitly.
EOF
    exit 2
  fi
  SOURCE_PROOF="$(SPACE_REPO="$SPACE_REPO" RAW_BUCKET="$RAW_BUCKET" \
    PINNED_IMPORT_REPORT="$PINNED_IMPORT_REPORT" python3 <<'PY'
import json
import os
import re

from huggingface_hub import HfApi

with open(os.environ["PINNED_IMPORT_REPORT"], encoding="utf-8") as handle:
    report = json.load(handle)
if report.get("reconciliation", {}).get("passed") is not True:
    raise SystemExit("pinned import report did not pass reconciliation")
target = report.get("target", {})
if target.get("bucket") != os.environ["RAW_BUCKET"]:
    raise SystemExit("pinned import report targets a different raw Bucket")
if not isinstance(target.get("objects"), int) or target["objects"] < 1:
    raise SystemExit("pinned import report has no imported objects")
if re.fullmatch(r"[a-f0-9]{64}", str(target.get("snapshot_revision", ""))) is None:
    raise SystemExit("pinned import report has no valid snapshot revision")
source = report.get("source", {})
source_repo = str(source.get("dataset", ""))
source_revision = str(source.get("revision", ""))
if re.fullmatch(r"[a-f0-9]{40}", source_revision) is None:
    raise SystemExit("pinned import report has no valid dataset revision")
api = HfApi()
variables = dict(api.get_space_variables(os.environ["SPACE_REPO"]))
if "DATASET_REPO" in variables:
    raise SystemExit(
        "legacy writers are not cut over: pause the Space, suspend enrichment, "
        "pin and import the final dataset revision, then remove DATASET_REPO in the "
        "coordinated cutover before using this deployment script"
    )
if api.dataset_info(source_repo).sha != source_revision:
    raise SystemExit("dataset advanced after the pinned import; quiesce writers and re-import")
print(source_repo)
print(source_revision)
PY
)"
  SOURCE_DATASET="$(head -n 1 <<<"$SOURCE_PROOF")"
  SOURCE_REVISION="$(tail -n 1 <<<"$SOURCE_PROOF")"
  echo "==> Re-verifying the pinned source against every current raw Bucket object"
  VERIFY_REPORT="$INDEX_WORK/verified-report.json"
  HF_TOKEN="$XTAP_STORAGE_TOKEN" npm --prefix "$ROOT" run storage:verify -- \
    --dataset "$SOURCE_DATASET" \
    --revision "$SOURCE_REVISION" \
    --raw-bucket "$RAW_BUCKET" \
    --report "$VERIFY_REPORT" \
    --work-dir "$INDEX_WORK/verify"
else
  echo "==> Initializing canonical raw configuration for the confirmed new pool"
  DATA_DIR="$INDEX_WORK" \
  RAW_BUCKET="$RAW_BUCKET" \
  HF_TOKEN="$XTAP_STORAGE_TOKEN" \
  ALLOWED_USERS="$ALLOWED_USERS" \
  POOL_ADMINS="${POOL_ADMINS:-$ALLOWED_USERS}" \
  npm --prefix "$ROOT" run storage:initialize
fi

echo "==> Bootstrapping and verifying the durable index"
DATA_DIR="$INDEX_WORK" \
RAW_BUCKET="$RAW_BUCKET" \
INDEX_BUCKET="$INDEX_BUCKET" \
HF_TOKEN="$XTAP_STORAGE_TOKEN" \
LLM_MODEL="$LLM_MODEL" \
TAXONOMY_VERSION="$TAXONOMY_VERSION" \
npm --prefix "$ROOT" run index:bootstrap

echo "==> Setting Space secrets and variables"
SPACE_REPO="$SPACE_REPO" \
RAW_BUCKET="$RAW_BUCKET" \
INDEX_BUCKET="$INDEX_BUCKET" \
ALLOWED_USERS="$ALLOWED_USERS" \
LLM_MODEL="$LLM_MODEL" \
TAXONOMY_VERSION="$TAXONOMY_VERSION" \
XTAP_STORAGE_TOKEN="$XTAP_STORAGE_TOKEN" \
python3 <<'PY'
import os
import secrets

from huggingface_hub import HfApi

space = os.environ["SPACE_REPO"]
api = HfApi()
variables = dict(api.get_space_variables(space))
for name in (
    "RAW_BUCKET",
    "INDEX_BUCKET",
    "ALLOWED_USERS",
    "LLM_MODEL",
    "TAXONOMY_VERSION",
):
    api.add_space_variable(space, name, os.environ[name])
api.add_space_secret(space, "HF_TOKEN", os.environ["XTAP_STORAGE_TOKEN"])
# Secrets cannot be listed back, so a sentinel variable marks that they were
# set once. Never rotate silently: rotating logs everyone out or disconnects
# every extension.
if "SECRETS_INITIALIZED" not in variables:
    for name in ("POOL_SIGNING_SECRET", "SESSION_SECRET"):
        api.add_space_secret(space, name, secrets.token_hex(32))
    api.add_space_variable(space, "SECRETS_INITIALIZED", "1")
print("Set Bucket variables, contract variables, and the scoped HF_TOKEN.")
PY

echo "==> Staging Space contents"
git -C "$ROOT" archive HEAD | tar -x -C "$STAGE"
cp "$ROOT/space/hf-space-README.md" "$STAGE/README.md"
rm -rf "$STAGE/docs" "$STAGE/extension"

echo "==> Uploading to $SPACE_REPO"
hf upload "$SPACE_REPO" "$STAGE" . --repo-type space --commit-message "deploy: $(git -C "$ROOT" rev-parse --short HEAD)"

if [[ "${ALLOW_EMPTY_POOL:-0}" != "1" ]]; then
  echo "==> Removing the retired dataset variable after replacement code is durable"
  SPACE_REPO="$SPACE_REPO" python3 <<'PY'
import os
from huggingface_hub import HfApi

api = HfApi()
variables = dict(api.get_space_variables(os.environ["SPACE_REPO"]))
if "DATASET_REPO" in variables:
    api.delete_space_variable(os.environ["SPACE_REPO"], key="DATASET_REPO")
PY
fi

echo "==> Done. Space: https://huggingface.co/spaces/$SPACE_REPO"