Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions .claude/skills/deploy-cuga/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
---
name: deploy-cuga
description: Ship cuga-apps changes to the running deployments — refresh the LOCAL all-in-one container (docker compose on :8080), build+push the CODE ENGINE image and roll the `cuga-agent-apps` service, and rebuild+publish the HUGGING FACE Space. Use when the user wants to deploy, ship, release, or "push live" changes to the gallery / CE / HF, or to update the local container to reflect current code. Builds compile from the current working tree (no commit required).
---

# Deploy cuga-apps (local + Code Engine + Hugging Face)

This skill ships the current working tree to three targets via the bundled
orchestrator `deploy.sh`. Each target is independent; CE always runs before HF
because the HF static build bakes in the CE service URL.

| Target | What it does | Result |
|---|---|---|
| `local` | `build/docker-compose.yml` → rebuild + run the all-in-one container | http://localhost:8080 |
| `ce` | `build/ce/build_and_push.sh` → push image to ICR, then `build/ce/deploy.sh` → roll `cuga-agent-apps` | the CE gallery URL |
| `hf` | `build/hf/build.sh` → static UI (bakes CE URL), then clone the HF Space, copy `dist/`, commit + push | `https://<owner>-<space>.hf.space/` |

The all-in-one image bundles the UI + ship-ready apps + the 5 internal MCP
servers + the stats collector — so CE/local pick up app, MCP, collector, AND UI
changes in one image. (The standalone `cuga-apps-mcp-*` servers in
`build/mcp_servers/` are a SEPARATE deploy — not part of this skill.)

## Steps to follow

1. **Scope.** Default to all three (`local ce hf`). If the user named specific
targets (e.g. "just CE", "CE and HF"), use only those.

2. **Show the plan first.** Run a dry-run so the user sees exactly what will
happen, then proceed (invoking this skill is the go-ahead to deploy):
```bash
.claude/skills/deploy-cuga/deploy.sh --dry-run <targets>
```

3. **Check prerequisites** for the chosen targets and surface anything missing
*before* the real run (don't half-deploy):
- `local` / `ce` build: `docker` running.
- `ce`: `ibmcloud` logged in, a CE project selected
(`ibmcloud ce project current`), and `ibmcloud cr login` done (registry
push). The script checks the project; registry auth fails loudly if absent.
- `hf`: push auth — either `HF_TOKEN` (a write token) exported, or working
git credentials / SSH for `huggingface.co`. Without either, the clone/push
fails with a clear message. The Space defaults to `anupamamurthi/cuga-agent-apps`
(override with `HF_SPACE`).

4. **Run it** for the chosen targets:
```bash
.claude/skills/deploy-cuga/deploy.sh <targets>
```
The script prints a per-target succeeded/failed summary and exits non-zero
if any target failed. The builds are heavy (the all-in-one image pre-pulls
model weights) — expect several minutes; don't abort early.

5. **Verify** each target that ran, and report the live URL:
- `local`: `curl -sf -o /dev/null -w "%{http_code}\n" http://localhost:8080/`
(expect 200). `docker compose -f build/docker-compose.yml ps` to confirm up.
- `ce`: `deploy.sh` prints the service URL; hit `<url>/` (expect 200). The
gallery host is the `cuga-agent-apps.…codeengine.appdomain.cloud` URL.
- `hf`: confirm the push landed; the Space rebuilds automatically at
`https://<owner>-<space>.hf.space/` (give it a minute).

6. **Report** the outcome plainly: which targets succeeded, the live URLs, and
any failure with the script's error output. If a target failed, do NOT
claim the deploy is live.

## Notes & overrides

- Useful env overrides (pass inline): `IMAGE_TAG`, `NAMESPACE`, `APP_NAME` (CE);
`HF_SPACE`, `HF_USER`, `HF_TOKEN`, `ALLINONE_BASE` (HF). See `deploy.sh -h`.
- These deploys are **outward-facing**. If the user invoked the skill with no
clear scope or seems unsure, confirm scope before the real run; otherwise the
invocation is sufficient authorization.
- Builds use the working tree directly — no `git commit`/`pull` is required
first (matches `build/DEPLOYMENT.md`).
- Secrets live in `build/.env` (gitignored) and the CE `app-env` secret — never
commit them and never echo their values.
149 changes: 149 additions & 0 deletions .claude/skills/deploy-cuga/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#!/usr/bin/env bash
# =====================================================================
# deploy.sh — ship cuga-apps to one or more targets:
#
# local refresh the local all-in-one (docker compose) on :8080
# ce build+push the all-in-one image and roll the Code Engine
# service `cuga-agent-apps`
# hf rebuild the static umbrella UI and publish it to the
# Hugging Face Space (it links into the CE service)
#
# Usage:
# ./deploy.sh # all three (local, ce, hf)
# ./deploy.sh ce # just Code Engine
# ./deploy.sh ce hf # CE then HF
# ./deploy.sh local # just the local container
# ./deploy.sh --dry-run all # print the plan, run nothing
#
# Order note: when both `ce` and `hf` run, CE goes first — the HF build
# bakes in the CE service URL, so CE should be live first.
#
# Env overrides:
# # Code Engine / image
# NAMESPACE ICR namespace (default: routing_namespace)
# IMAGE_TAG image tag (default: latest)
# APP_NAME CE app name (default: cuga-agent-apps)
# # Hugging Face
# HF_SPACE owner/space (default: anupamamurthi/cuga-agent-apps)
# HF_USER git username for push (default: owner part of HF_SPACE)
# HF_TOKEN HF write token; if set it's used for the push, else your
# cached git credentials / SSH are used
# ALLINONE_BASE CE base URL baked into the HF build (default: the CE
# host for APP_NAME; see build/hf/build.sh)
# =====================================================================
set -uo pipefail

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # .claude/skills/deploy-cuga
REPO_ROOT="$(cd "$HERE/../../.." && pwd)" # repo root

DRY=0
TARGETS=()
for a in "$@"; do
case "$a" in
--dry-run) DRY=1 ;;
all) TARGETS+=(local ce hf) ;;
local|ce|hf) TARGETS+=("$a") ;;
-h|--help) sed -n '2,/^# ===/p' "$0" | sed 's/^# \?//'; exit 0 ;;
*) echo "ERROR: unknown target '$a' (want: local | ce | hf | all)" >&2; exit 2 ;;
esac
done
[[ ${#TARGETS[@]} -eq 0 ]] && TARGETS=(local ce hf)

# De-dupe while keeping a stable order, and force ce-before-hf.
have() { printf '%s\n' "${TARGETS[@]}" | grep -qx "$1"; }
ORDERED=()
for t in local ce hf; do have "$t" && ORDERED+=("$t"); done
TARGETS=("${ORDERED[@]}")

say() { echo "── $* ──"; }
run() { echo "+ $*"; [[ $DRY == 1 ]] && return 0; "$@"; }
die() { echo "ERROR: $*" >&2; exit 1; }
need() { command -v "$1" >/dev/null 2>&1 || die "$1 not on PATH ($2)"; }

echo "════════════════════════════════════════════════════════════════"
echo " cuga-apps deploy"
echo " repo : $REPO_ROOT"
echo " targets : ${TARGETS[*]}"
echo " dry-run : $([[ $DRY == 1 ]] && echo yes || echo no)"
echo "════════════════════════════════════════════════════════════════"

declare -a OK=() FAIL=()
mark() { if [[ $1 == 0 ]]; then OK+=("$2"); else FAIL+=("$2"); fi; }

# ── local: rebuild + run the all-in-one container on :8080 ────────────
do_local() {
say "LOCAL — rebuild + run all-in-one (docker compose) on :8080"
need docker "local container build"
( cd "$REPO_ROOT/build" || exit 1
[[ -f .env ]] || { [[ -f .env.example ]] && cp .env.example .env && echo " (created build/.env from .env.example — add keys)"; }
run docker compose up --build -d
)
}

# ── ce: build+push the image, then roll the CE service ───────────────
do_ce() {
say "CODE ENGINE — build+push image, then deploy cuga-agent-apps"
need docker "image build"
need ibmcloud "Code Engine deploy"
if [[ $DRY == 0 ]]; then
ibmcloud ce project current >/dev/null 2>&1 \
|| die "no Code Engine project selected — run: ibmcloud ce project select --name <project>"
fi
run "$REPO_ROOT/build/ce/build_and_push.sh" || return 1
run "$REPO_ROOT/build/ce/deploy.sh" || return 1
}

# ── hf: rebuild the static UI, then push it to the HF Space ───────────
do_hf() {
say "HUGGING FACE — rebuild static umbrella UI, publish to Space"
need git "HF Space push"
local space="${HF_SPACE:-anupamamurthi/cuga-agent-apps}"
local user="${HF_USER:-${space%%/*}}"

# Build the static SPA (ALLINONE_BASE, if set, is baked in).
if [[ -n "${ALLINONE_BASE:-}" ]]; then
run env ALLINONE_BASE="$ALLINONE_BASE" "$REPO_ROOT/build/hf/build.sh" || return 1
else
run "$REPO_ROOT/build/hf/build.sh" || return 1
fi
[[ $DRY == 1 ]] && { echo "+ (dry-run) would publish build/hf/dist → $space"; return 0; }

local url="https://huggingface.co/spaces/$space"
[[ -n "${HF_TOKEN:-}" ]] && url="https://${user}:${HF_TOKEN}@huggingface.co/spaces/$space"

local tmp; tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' RETURN
echo "+ git clone $space"
git clone --depth 1 "$url" "$tmp" \
|| die "could not clone HF Space $space — set HF_TOKEN or configure git/HF auth"
# Replace the Space contents with the fresh build (keep .git).
find "$tmp" -mindepth 1 -maxdepth 1 ! -name .git -exec rm -rf {} +
cp -r "$REPO_ROOT/build/hf/dist/." "$tmp/"
( cd "$tmp" || exit 1
git add -A
if git diff --cached --quiet; then
echo " (no changes to publish)"
else
git commit -m "deploy: update umbrella UI ($(date -u +%Y-%m-%dT%H:%MZ))" >/dev/null
git push || die "git push to HF Space failed — check HF_TOKEN / credentials"
echo " ✓ pushed to $space"
fi
)
}

for t in "${TARGETS[@]}"; do
echo
case "$t" in
local) do_local; mark $? local ;;
ce) do_ce; mark $? ce ;;
hf) do_hf; mark $? hf ;;
esac
done

echo
echo "════════════════════════════════════════════════════════════════"
echo " Summary"
echo " succeeded: ${OK[*]:-<none>}"
echo " failed : ${FAIL[*]:-<none>}"
echo "════════════════════════════════════════════════════════════════"
[[ ${#FAIL[@]} -eq 0 ]] || exit 1
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,10 @@ cuga-apps/apps/arch_diagram/diagrams.db
cuga-apps/apps/ibm_whats_new/.store.json
cuga-apps/apps/web_researcher/.store.json
CUGA_OpenClaw_NemoClaw_DeepAgents_Smolagents_Hermes_Comparison_Revised.docx
.terminal
.terminal
cuga-apps/docs/architecture_app_anatomy_cloud_advisor.png
cuga-apps/apps/__pycache__/_llm.cpython-311.pyc
cuga-apps/apps/__pycache__/__init__.cpython-311.pyc
cuga-apps/apps/__pycache__/_llm.cpython-311.pyc
cuga-apps/apps/__pycache__/_llm.cpython-311.pyc
*.pyc
Loading