File size: 5,636 Bytes
7a7d4fa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env bash
# Deploy the FastMCP HTTP server to Cloud Run (inferpoker / us-west1).
# Does not deploy to GCP_PROJECT_ID from .env (that is a different project).
# Does not --reset-chroma, re-transcribe, or touch the podcast_downloads rsync.
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${ROOT}"

PROJECT="${GCP_BACKUP_PROJECT:-inferpoker}"
REGION="${GCP_REGION:-us-west1}"
if [[ "${PROJECT}" == "inferpoker" ]]; then
  echo "Refusing to deploy Learning Focused onto GCP project inferpoker." >&2
  echo "Use ./scripts/deploy_hf.sh (Hugging Face Space). InferPoker billing is off-limits." >&2
  exit 2
fi
SERVICE="${CLOUD_RUN_SERVICE:-learningfocused-mcp}"
SA_NAME="${CLOUD_RUN_SA:-learningfocused-mcp}"
SA_EMAIL="${SA_NAME}@${PROJECT}.iam.gserviceaccount.com"
BUCKET="${GCS_CORPUS_BUCKET:-inferpoker-learningfocused}"
AR_REPO="${AR_REPO:-learningfocused}"
SECRET_OPENAI="${SECRET_OPENAI:-learningfocused-openai-api-key}"
SECRET_GOOGLE="${SECRET_GOOGLE:-learningfocused-google-api-key}"
SECRET_ASSEMBLYAI="${SECRET_ASSEMBLYAI:-learningfocused-assemblyai-api-key}"
ENV_FILE="${ROOT}/.env"
UPDATE_SECRETS="${UPDATE_SECRETS:-0}"

if [[ ! -f "${ENV_FILE}" ]]; then
  echo "Missing ${ENV_FILE} (needed to seed Secret Manager). Not committed." >&2
  exit 2
fi

gcloud config set project "${PROJECT}" >/dev/null

echo ">> Enabling APIs on ${PROJECT}"
gcloud services enable \
  run.googleapis.com \
  secretmanager.googleapis.com \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com \
  storage.googleapis.com \
  --project="${PROJECT}"

if ! gcloud artifacts repositories describe "${AR_REPO}" --location="${REGION}" --project="${PROJECT}" >/dev/null 2>&1; then
  echo ">> Creating Artifact Registry ${AR_REPO}"
  gcloud artifacts repositories create "${AR_REPO}" \
    --repository-format=docker \
    --location="${REGION}" \
    --description="LearningFocused MCP" \
    --project="${PROJECT}"
fi

if ! gcloud iam service-accounts describe "${SA_EMAIL}" --project="${PROJECT}" >/dev/null 2>&1; then
  echo ">> Creating service account ${SA_EMAIL}"
  gcloud iam service-accounts create "${SA_NAME}" \
    --display-name="LearningFocused MCP Cloud Run" \
    --project="${PROJECT}"
fi

echo ">> Granting ${SA_EMAIL} objectViewer on gs://${BUCKET}"
gcloud storage buckets add-iam-policy-binding "gs://${BUCKET}" \
  --member="serviceAccount:${SA_EMAIL}" \
  --role="roles/storage.objectViewer" \
  --project="${PROJECT}" >/dev/null

read_env_value() {
  local key="$1"
  uv run python - "$ENV_FILE" "$key" <<'PY'
import sys
from pathlib import Path
from dotenv import dotenv_values
path, key = sys.argv[1], sys.argv[2]
vals = dotenv_values(path)
value = vals.get(key)
if not value:
    raise SystemExit(f"Missing {key} in {path}")
sys.stdout.write(value)
PY
}

ensure_secret() {
  local name="$1"
  local env_key="$2"
  if gcloud secrets describe "${name}" --project="${PROJECT}" >/dev/null 2>&1; then
    if [[ "${UPDATE_SECRETS}" != "1" ]]; then
      echo ">> Secret ${name} exists (set UPDATE_SECRETS=1 to add a version)"
      return 0
    fi
  else
    echo ">> Creating secret ${name}"
    gcloud secrets create "${name}" --replication-policy=automatic --project="${PROJECT}"
  fi
  echo ">> Adding secret version ${name} from .env ${env_key}"
  read_env_value "${env_key}" | gcloud secrets versions add "${name}" --data-file=- --project="${PROJECT}" >/dev/null
}

ensure_secret "${SECRET_OPENAI}" "OPENAI_API_KEY"
ensure_secret "${SECRET_GOOGLE}" "GOOGLE_API_KEY"
ensure_secret "${SECRET_ASSEMBLYAI}" "ASSEMBLYAI_API_KEY"

for secret in "${SECRET_OPENAI}" "${SECRET_GOOGLE}" "${SECRET_ASSEMBLYAI}"; do
  echo ">> Granting secretAccessor on ${secret}"
  gcloud secrets add-iam-policy-binding "${secret}" \
    --member="serviceAccount:${SA_EMAIL}" \
    --role="roles/secretmanager.secretAccessor" \
    --project="${PROJECT}" >/dev/null
done

PROJECT_NUMBER="$(gcloud projects describe "${PROJECT}" --format='value(projectNumber)')"
COMPUTE_SA="${PROJECT_NUMBER}-compute@developer.gserviceaccount.com"
echo ">> Cloud Run --source builds as ${COMPUTE_SA} (project Editor). Runtime SA is ${SA_EMAIL}."

echo ">> Deploying ${SERVICE} to Cloud Run (${REGION})"
gcloud run deploy "${SERVICE}" \
  --quiet \
  --source "${ROOT}" \
  --project="${PROJECT}" \
  --region="${REGION}" \
  --service-account="${SA_EMAIL}" \
  --allow-unauthenticated \
  --min-instances=1 \
  --max-instances=3 \
  --memory=2Gi \
  --cpu=1 \
  --timeout=300 \
  --cpu-boost \
  --no-cpu-throttling \
  --port=8080 \
  --set-env-vars="MCP_HOST=0.0.0.0,HYDRATE_CHROMA=1,GCS_CHROMA_BUCKET=${BUCKET},GCP_BACKUP_PROJECT=${PROJECT},CHROMA_DIR=/data/chroma_db,MCP_RATE_LIMIT_PER_MINUTE=60,MCP_SNIPPET_LIMIT=32768" \
  --set-secrets="OPENAI_API_KEY=${SECRET_OPENAI}:latest,GOOGLE_API_KEY=${SECRET_GOOGLE}:latest,ASSEMBLYAI_API_KEY=${SECRET_ASSEMBLYAI}:latest"

echo ">> Ensuring allUsers can invoke (public MCP)"
gcloud run services add-iam-policy-binding "${SERVICE}" \
  --region="${REGION}" \
  --member="allUsers" \
  --role="roles/run.invoker" \
  --project="${PROJECT}" >/dev/null

URL="$(gcloud run services describe "${SERVICE}" --region="${REGION}" --project="${PROJECT}" --format='value(status.url)')"
echo
echo "Service:  ${SERVICE}"
echo "Region:   ${REGION}"
echo "URL:      ${URL}"
echo "MCP:      ${URL}/mcp"
echo "Health:   ${URL}/health"
echo "Auth:     allUsers (unauthenticated)"
echo "Min inst: 1"
echo "Secrets:  ${SECRET_OPENAI}, ${SECRET_GOOGLE}, ${SECRET_ASSEMBLYAI}"
echo
echo "Cursor mcp.json:"
cat <<EOF
{
  "mcpServers": {
    "learningfocused": {
      "url": "${URL}/mcp"
    }
  }
}
EOF