# Scripts bash listos para usar Copia a `/opt/scripts/` en el VPS Debian. ## `oasis-scout.sh` ```bash #!/bin/bash set -euo pipefail REPO=/opt/oasis_mobile REPORTS=/var/oasis-auto/reports mkdir -p "$REPORTS" cd "$REPO" git fetch upstream 2>&1 DATE=$(date +%Y-%m-%d) claude -p "$(cat /opt/scripts/prompts/scout.md)" \ --allowed-tools "Read,Write,Bash" \ > "$REPORTS/$DATE-scout-claude.log" 2>&1 # El reporte JSON y MD se generan por claude if [ -f "$REPORTS/$DATE-scout.json" ]; then bash /opt/scripts/notify-telegram.sh "📊 Scout listo para $DATE. Ver: https://0asis.net/admin/reports/$DATE" else bash /opt/scripts/notify-telegram.sh "❌ Scout falló para $DATE. Log: $REPORTS/$DATE-scout-claude.log" exit 1 fi ``` ## `oasis-merger.sh` ```bash #!/bin/bash set -euo pipefail REPO=/opt/oasis_mobile REPORTS=/var/oasis-auto/reports DATE=$(date +%Y-%m-%d) THRESHOLD=${AUTO_APPROVE_THRESHOLD:-1.0} cd "$REPO" # Verificar que existe scout reciente if [ ! -f "$REPORTS/$DATE-scout.json" ]; then bash /opt/scripts/notify-telegram.sh "⚠️ Merger: no hay scout.json para $DATE. Saltando." exit 0 fi # Leer conflict_score SCORE=$(jq -r '.conflict_score' "$REPORTS/$DATE-scout.json") APPROVED=$(jq -r '.approved // false' "$REPORTS/$DATE-scout.json") if [ "$APPROVED" != "true" ] && (( $(echo "$SCORE > $THRESHOLD" | bc -l) )); then bash /opt/scripts/notify-telegram.sh "⏸ Merger: conflict_score $SCORE > $THRESHOLD, esperando aprobación manual." exit 0 fi # Procede con merge claude -p "$(cat /opt/scripts/prompts/merger.md)" \ --allowed-tools "Read,Write,Edit,Bash" \ > "$REPORTS/$DATE-merger-claude.log" 2>&1 if [ $? -eq 0 ]; then bash /opt/scripts/notify-telegram.sh "✅ Merger OK para $DATE. Lista para build." else bash /opt/scripts/notify-telegram.sh "❌ Merger falló para $DATE." fi ``` ## `oasis-builder.sh` ```bash #!/bin/bash set -euo pipefail REPO=/opt/oasis_mobile APKS=/var/www/0asis.net/testing-app DATE=$(date +%Y%m%d) mkdir -p "$APKS" cd "$REPO" # Verificar working tree limpio if [ -n "$(git status --porcelain)" ]; then bash /opt/scripts/notify-telegram.sh "⚠️ Builder: working tree no limpio. Saltando." exit 1 fi # Verificar no hay markers de conflicto if grep -rn "<<<<<<< HEAD" "$REPO/nodejs-project/" 2>/dev/null; then bash /opt/scripts/notify-telegram.sh "❌ Builder: hay conflictos sin resolver." exit 1 fi # Build zip cd "$REPO/nodejs-project" zip -0 -r /tmp/nodejs-project-new.zip nodejs-project/ \ -x "*.apk" "*/.git/*" > /tmp/zip.log 2>&1 # Build APK mkdir -p /tmp/assets cp /tmp/nodejs-project-new.zip /tmp/assets/nodejs-project.zip cp /opt/oasis-base/oasis-v0.6.8.apk /tmp/oasis-temp.apk zip -d /tmp/oasis-temp.apk "META-INF/*" >/dev/null cd /tmp && zip -0 oasis-temp.apk assets/nodejs-project.zip >/dev/null SDK=/opt/android-sdk/build-tools/35.0.1 $SDK/zipalign -f -p 4 /tmp/oasis-temp.apk /tmp/oasis-aligned.apk $SDK/apksigner sign \ --ks /opt/secrets/oasis-release-key.jks \ --ks-pass pass:oasis123 \ --key-pass pass:oasis123 \ --ks-key-alias oasis \ /tmp/oasis-aligned.apk $SDK/apksigner verify /tmp/oasis-aligned.apk || { bash /opt/scripts/notify-telegram.sh "❌ APK signature inválida." exit 1 } # Detectar versión del package.json VERSION=$(jq -r '.version' "$REPO/nodejs-project/nodejs-project/src/server/package.json") # Borrar pruebas anteriores y copiar rm -f "$APKS/oasis-*-pruebas.apk" NAME="oasis-v$VERSION-$DATE-pruebas.apk" cp /tmp/oasis-aligned.apk "$APKS/$NAME" ln -sf "$NAME" "$APKS/latest.apk" SIZE=$(du -h "$APKS/$NAME" | cut -f1) bash /opt/scripts/notify-telegram.sh "📱 APK $NAME ($SIZE) lista: https://0asis.net/testing-app/$NAME" ``` ## `notify-telegram.sh` ```bash #!/bin/bash TOKEN="${TELEGRAM_BOT_TOKEN:-}" CHAT="${TELEGRAM_CHAT_ID:-}" MSG="${1:-empty}" if [ -z "$TOKEN" ] || [ -z "$CHAT" ]; then echo "Telegram no configurado, mensaje:" echo "$MSG" exit 0 fi curl -s -X POST "https://api.telegram.org/bot$TOKEN/sendMessage" \ -d "chat_id=$CHAT" \ -d "text=$MSG" \ -d "parse_mode=Markdown" > /dev/null ``` ## `/etc/cron.d/oasis-auto` ```cron # Oasis Mobile auto-update pipeline SHELL=/bin/bash PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxxx TELEGRAM_BOT_TOKEN=123456789:AAxxxx TELEGRAM_CHAT_ID=-1001234567890 AUTO_APPROVE_THRESHOLD=0.5 0 3 * * 1 oasis /opt/scripts/oasis-scout.sh 0 4 * * 1 oasis /opt/scripts/oasis-merger.sh 0 5 * * 1 oasis /opt/scripts/oasis-builder.sh ``` ## Webhook receiver (Python Flask) ```python # /opt/oasis-webhook/app.py from flask import Flask, request, abort import hmac, hashlib, subprocess, os app = Flask(__name__) SECRET = os.environ['WEBHOOK_SECRET'].encode() @app.post("/api/oasis-release") def release(): sig = request.headers.get("X-Hub-Signature-256", "") mac = hmac.new(SECRET, request.data, hashlib.sha256) expected = "sha256=" + mac.hexdigest() if not hmac.compare_digest(sig, expected): abort(401) event = request.headers.get("X-GitHub-Event") if event != "release": return "skip", 200 payload = request.json if payload.get("action") == "published": tag = payload["release"]["tag_name"] subprocess.Popen([ "sudo", "-u", "oasis", "/opt/scripts/oasis-scout.sh", "--tag", tag, "--reason", "webhook" ]) return "ok", 200 ``` ## `/etc/systemd/system/oasis-webhook.service` ```ini [Unit] Description=Oasis webhook receiver After=network.target [Service] User=oasis WorkingDirectory=/opt/oasis-webhook EnvironmentFile=/opt/secrets/webhook.env ExecStart=/usr/bin/python3 -m gunicorn -b 127.0.0.1:5000 app:app Restart=always [Install] WantedBy=multi-user.target ``` ## `/etc/caddy/Caddyfile` ``` 0asis.net { encode gzip zstd handle /api/oasis-release { reverse_proxy 127.0.0.1:5000 } handle_path /testing-app/* { root * /var/www/0asis.net/testing-app file_server browse @apk path *.apk header @apk Content-Disposition "attachment" } handle { root * /var/www/0asis.net file_server try_files {path} {path}/ /index.html } } ``` ## Setup inicial — `bootstrap.sh` ```bash #!/bin/bash # Ejecutar UNA vez al preparar el VPS Debian set -euo pipefail # 1. User dedicado useradd -m -s /bin/bash oasis || true # 2. Directorios mkdir -p /opt/oasis_mobile /opt/scripts /opt/secrets /opt/oasis-base mkdir -p /var/oasis-auto/reports /var/www/0asis.net/testing-app # 3. Dependencias del sistema apt update apt install -y git curl jq bc python3-pip nginx-light \ openjdk-17-jdk-headless zip unzip cron # 4. Caddy curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/setup.deb.sh' | bash apt install -y caddy # 5. Android SDK build-tools mkdir -p /opt/android-sdk cd /opt/android-sdk curl -L -o cmdline-tools.zip \ "https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip" unzip cmdline-tools.zip cmdline-tools/bin/sdkmanager --sdk_root=/opt/android-sdk \ "build-tools;35.0.1" # 6. Claude Code curl -fsSL https://claude.ai/install.sh | bash # 7. Permisos chown -R oasis:oasis /opt/oasis_mobile /var/oasis-auto /opt/scripts # 8. Clonar repo (necesita SSH key configurada) sudo -u oasis git clone git@code.03c8.net:s1to/oasis_mobile.git /opt/oasis_mobile cd /opt/oasis_mobile sudo -u oasis git remote add upstream https://github.com/epsylon/oasis.git echo "Bootstrap completo. Configura:" echo " - /opt/secrets/oasis-release-key.jks (keystore)" echo " - /etc/cron.d/oasis-auto (con tu ANTHROPIC_API_KEY)" echo " - DNS de 0asis.net apuntando a este server" echo " - /etc/caddy/Caddyfile" echo " - systemctl enable --now oasis-webhook" ```