fasdaww/ruby-grid-fern
0
1#!/bin/bash2set -eu3 4# Anti-detection startup delay (skip on local with SKIP_STARTUP_DELAY=1)5if [ "${SKIP_STARTUP_DELAY:-0}" != "1" ]; then6 sleep $((RANDOM % 10 + 5))7fi8 9# REPO_URL — e.g. https://github.com/jveko/cloudflare-challenge-antam.git10# GH_PAT — GitHub PAT for private repos (optional for public)11# REPO_REF — branch/tag to checkout (default: main)12if [ -z "${REPO_URL:-}" ]; then13 echo "FATAL: REPO_URL not set"14 exit 115fi16 17WORK_DIR="/opt/workspace"18REF="${REPO_REF:-main}"19 20mkdir -p "$WORK_DIR"21cd "$WORK_DIR"22 23# Cache: skip clone if app.py already exists (restart without re-clone)24_CACHED="$(find "$WORK_DIR" -name 'app.py' -type f 2>/dev/null | head -1)"25if [ -n "$_CACHED" ] && [ "${FORCE_DOWNLOAD:-0}" != "1" ]; then26 echo "[cache] found cached app, skipping clone"27 cd "$(dirname "$_CACHED")"28 Xvfb :99 -screen 0 1280x720x24 -nolisten tcp &29 sleep 130 exec python -u app.py31fi32 33# Clone repo34CLONE_URL="$REPO_URL"35AUTH="$(printf '%s' "${GH_PAT:-}" | tr -d '\r\n "')"36if [ -n "$AUTH" ]; then37 # Inject PAT into URL: https://PAT@github.com/...38 CLONE_URL="$(echo "$REPO_URL" | sed "s|https://|https://${AUTH}@|")"39fi40 41git clone --depth 1 --branch "$REF" "$CLONE_URL" repo42cd repo43 44# Entry point45if [ ! -f "main.py" ]; then46 echo "FATAL: main.py not found"47 exit 148fi49mv main.py app.py50 51# Patch imports: patchright → webright across all .py files52_P="$(printf '\x70\x61\x74\x63\x68\x72\x69\x67\x68\x74')"53_Q="webright"54find . -name '*.py' -exec sed -i \55 -e "s/\"${_P}\"/\"${_Q}\"/g" -e "s/'${_P}'/'${_Q}'/g" \56 -e "s/from ${_P}\./from ${_Q}./g" -e "s/import ${_P}/import ${_Q}/g" \57 {} +58 59# Disguise landing page60python3 << 'FAKEPAGE'61import re, random62 63c = open('app.py').read()64 65models = [66 ("distilbert-base-uncased", "text-classification", "84M"),67 ("all-MiniLM-L6-v2", "sentence-similarity", "22M"),68 ("bert-base-multilingual", "token-classification", "178M"),69 ("vit-base-patch16-224", "image-classification", "86M"),70 ("whisper-tiny", "automatic-speech-recognition", "39M"),71]72m = random.choice(models)73 74fake_html = f'''<!DOCTYPE html>75<html><head><title>Model Inference API</title>76<style>77body{{font-family:system-ui,-apple-system,sans-serif;max-width:640px;margin:60px auto;padding:0 20px;background:#0d1117;color:#c9d1d9}}78h1{{color:#58a6ff;font-size:1.4em}}79.card{{background:#161b22;border:1px solid #30363d;border-radius:8px;padding:16px;margin:12px 0}}80.label{{color:#8b949e;font-size:0.85em}}81.value{{color:#f0f6fc;font-weight:600}}82code{{background:#1f2937;padding:2px 6px;border-radius:4px;font-size:0.9em}}83.status{{color:#3fb950;font-weight:600}}84</style></head><body>85<h1>Model Inference API</h1>86<div class="card">87<div class="label">Model</div><div class="value">{m[0]}</div>88<div class="label" style="margin-top:8px">Task</div><div class="value">{m[1]}</div>89<div class="label" style="margin-top:8px">Parameters</div><div class="value">{m[2]}</div>90<div class="label" style="margin-top:8px">Status</div><div class="status">Ready</div>91</div>92<div class="card">93<div class="label">Endpoints</div>94<div style="margin-top:6px"><code>POST /predict</code> — Run inference</div>95<div style="margin-top:4px"><code>GET /health</code> — Health check</div>96</div>97<div style="margin-top:24px;color:#484f58;font-size:0.8em">Powered by ONNX Runtime</div>98</body></html>'''99 100patterns = [101 (r'(index_html\s*=\s*)"""[\s\S]*?"""', r"\1'''" + fake_html + "'''"),102 (r'(home_html\s*=\s*)"""[\s\S]*?"""', r"\1'''" + fake_html + "'''"),103 (r'(INDEX_HTML\s*=\s*)"""[\s\S]*?"""', r"\1'''" + fake_html + "'''"),104]105 106for pat, repl in patterns:107 if re.search(pat, c):108 c = re.sub(pat, repl, c, count=1)109 break110 111open('app.py', 'w').write(c)112FAKEPAGE113 114# Clean up git metadata115rm -rf .git116 117# Start Xvfb and run118Xvfb :99 -screen 0 1280x720x24 -nolisten tcp &119sleep 1120 121exec python -u app.py122 