Spaces:
Paused
Paused
Download deploy/oracle/setup.sh from chandrakiran06/rosdiff: direct link, hf CLI and curl.
- Browser
- Download file 6.55 kB
-
https://huggingface.co/spaces/chandrakiran06/rosdiff/resolve/main/deploy/oracle/setup.sh
- Command line
-
hf download hf://spaces/chandrakiran06/rosdiff/deploy/oracle/setup.sh
-
curl -L -o setup.sh https://huggingface.co/spaces/chandrakiran06/rosdiff/resolve/main/deploy/oracle/setup.sh
6.55 kB
| # RosDiff on an Oracle Cloud "Always Free" Ubuntu machine (Ampere A1 / ARM, or x86). See deploy/oracle/README.md. | |
| # | |
| # Run it from the RosDiff folder on the machine: bash deploy/oracle/setup.sh | |
| # It is safe to run again (after copying an updated RosDiff over): it only does what is still missing, then | |
| # restarts the service. | |
| # | |
| # What it sets up: | |
| # - system packages, Python venv with RosDiff's requirements, Node.js + OpenCode (free models) | |
| # - the documentation search index (python -m rag.ingest), built once | |
| # - a systemd service that keeps RosDiff running and restarts it after crashes and reboots | |
| # - Caddy in front of it with a free HTTPS certificate: https://<public-ip-with-dashes>.sslip.io | |
| # (or your own domain: DOMAIN=rosdiff.example.com bash deploy/oracle/setup.sh) | |
| # - the machine's firewall opened for ports 80/443 (Oracle's Ubuntu images block them by default) | |
| # - a daily backup of the database (testers' work, budgets, feedback), 14 days kept | |
| set -euo pipefail | |
| APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" | |
| RUN_USER="$(id -un)" | |
| cd "$APP_DIR" | |
| say() { printf '\n\033[1;36m==> %s\033[0m\n' "$*"; } | |
| warn() { printf '\033[1;33m!! %s\033[0m\n' "$*"; } | |
| [[ $EUID -eq 0 ]] && { echo "Run as your user, not root (the script uses sudo where needed)"; exit 1; } | |
| [[ -f api/main.py ]] || { echo "Run this from the RosDiff folder (api/main.py not found)"; exit 1; } | |
| [[ -f .env ]] || { echo "Copy your .env into $APP_DIR first (see deploy/oracle/README.md, step 5)"; exit 1; } | |
| [[ -f testers.yaml ]] || warn "testers.yaml not found: nobody can sign in until you add testers (python -m api.keys add <name>)" | |
| say "System packages" | |
| sudo apt-get update -qq | |
| sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \ | |
| python3 python3-venv python3-pip python3-dev build-essential git curl sqlite3 nodejs npm iptables-persistent libgl1 \ | |
| debian-keyring debian-archive-keyring apt-transport-https gnupg >/dev/null | |
| if ! command -v caddy >/dev/null; then | |
| if ! sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq caddy >/dev/null 2>&1; then | |
| # not in this Ubuntu release's archive: use Caddy's official repository | |
| curl -1sLf https://dl.cloudsmith.io/public/caddy/stable/gpg.key | | |
| sudo gpg --batch --yes --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg | |
| curl -1sLf https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt | | |
| sudo tee /etc/apt/sources.list.d/caddy-stable.list >/dev/null | |
| sudo apt-get update -qq && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq caddy >/dev/null | |
| fi | |
| fi | |
| say "Python environment" | |
| [[ -d .venv ]] || python3 -m venv .venv | |
| .venv/bin/pip install -q --upgrade pip | |
| .venv/bin/pip install -q -r requirements.txt | |
| say "OpenCode (free prompt enhancement and free debugging)" | |
| if ! command -v opencode >/dev/null; then | |
| sudo npm install -g --silent opencode-ai || warn "OpenCode did not install; RosDiff still works, on paid models only" | |
| fi | |
| command -v opencode >/dev/null && opencode --version || true | |
| say "Documentation search index" | |
| if [[ ! -d data/chroma || ! -f data/ros2_interfaces.json ]]; then | |
| set -a; source .env; set +a | |
| .venv/bin/python -m rag.ingest # clones MuJoCo/ROS 2 docs and the robot models; a few minutes | |
| else | |
| echo "already built (delete data/chroma to rebuild)" | |
| fi | |
| say "Public address" | |
| if [[ -z "${DOMAIN:-}" ]]; then | |
| IP="$(curl -fsS --max-time 10 https://api.ipify.org || curl -fsS --max-time 10 https://ifconfig.me)" | |
| DOMAIN="${IP//./-}.sslip.io" | |
| fi | |
| URL="https://$DOMAIN" | |
| echo "$URL" | |
| # the app puts this address in report links | |
| if grep -q '^PUBLIC_BASE_URL=' .env; then | |
| existing="$(grep '^PUBLIC_BASE_URL=' .env | head -1 | cut -d= -f2-)" | |
| if [[ -z "$existing" || "$existing" == *sslip.io* ]]; then | |
| sed -i "s|^PUBLIC_BASE_URL=.*|PUBLIC_BASE_URL=$URL|" .env | |
| fi | |
| else | |
| printf '\nPUBLIC_BASE_URL=%s\n' "$URL" >> .env | |
| fi | |
| grep -q '^REPORT_SECRET=..' .env || { | |
| sed -i '/^REPORT_SECRET=/d' .env | |
| printf 'REPORT_SECRET=%s\n' "$(openssl rand -hex 32)" >> .env | |
| } | |
| chmod 600 .env | |
| say "RosDiff service" | |
| sudo tee /etc/systemd/system/rosdiff.service >/dev/null <<EOF | |
| [Unit] | |
| Description=RosDiff API and web app | |
| After=network-online.target | |
| Wants=network-online.target | |
| [Service] | |
| User=$RUN_USER | |
| WorkingDirectory=$APP_DIR | |
| # .env is read by bash (it has comments after values, which systemd's EnvironmentFile would keep) | |
| ExecStart=/bin/bash -c 'set -a; source .env; set +a; exec .venv/bin/uvicorn api.main:app --host 127.0.0.1 --port 8000 --workers 1 --timeout-keep-alive 75' | |
| Restart=always | |
| RestartSec=5 | |
| Environment=PATH=$APP_DIR/.venv/bin:/usr/local/bin:/usr/bin:/bin | |
| Environment=HOME=$HOME | |
| [Install] | |
| WantedBy=multi-user.target | |
| EOF | |
| sudo systemctl daemon-reload | |
| sudo systemctl enable --now rosdiff >/dev/null | |
| sudo systemctl restart rosdiff | |
| say "HTTPS (Caddy)" | |
| sudo tee /etc/caddy/Caddyfile >/dev/null <<EOF | |
| $DOMAIN { | |
| encode gzip | |
| reverse_proxy 127.0.0.1:8000 | |
| } | |
| EOF | |
| sudo systemctl enable --now caddy >/dev/null | |
| sudo systemctl reload caddy || sudo systemctl restart caddy | |
| say "Firewall: allow 80 and 443" | |
| for port in 80 443; do | |
| if ! sudo iptables -C INPUT -p tcp --dport "$port" -m state --state NEW -j ACCEPT 2>/dev/null; then | |
| # insert above Oracle's catch-all REJECT rule | |
| pos="$(sudo iptables -L INPUT --line-numbers -n | awk '/REJECT/ {print $1; exit}')" | |
| sudo iptables -I INPUT "${pos:-1}" -p tcp --dport "$port" -m state --state NEW -j ACCEPT | |
| fi | |
| done | |
| sudo netfilter-persistent save >/dev/null | |
| say "Daily database backup" | |
| mkdir -p "$HOME/backups" | |
| DB="$APP_DIR/data/generations.sqlite3" | |
| CRON="15 3 * * * [ -f $DB ] && sqlite3 $DB \".backup '$HOME/backups/generations-\$(date +\\%F).sqlite3'\" && find $HOME/backups -name 'generations-*' -mtime +14 -delete" | |
| ( { crontab -l 2>/dev/null || true; } | { grep -v 'generations-' || true; } ; echo "$CRON" ) | crontab - | |
| say "Checking" | |
| for i in $(seq 1 30); do | |
| curl -fsS localhost:8000/health >/dev/null 2>&1 && break | |
| sleep 2 | |
| done | |
| if curl -fsS localhost:8000/health >/dev/null 2>&1; then | |
| curl -fsS localhost:8000/health | .venv/bin/python -c 'import json,sys; h=json.load(sys.stdin); print(json.dumps({k: h.get(k) for k in ("status","llm_configured","runs_enabled","prompt_enhance","index")}, indent=1))' | |
| else | |
| warn "RosDiff is not answering yet; see: journalctl -u rosdiff -n 50" | |
| fi | |
| echo | |
| echo "Done. Testers open: $URL" | |
| echo "(The HTTPS certificate can take a minute the first time. If the page doesn't load, check step 3 of the guide:" | |
| echo " ports 80 and 443 must be open in the Oracle security list.)" | |