Agent kodujący mówi, że funkcja działa. Może i tak. Testy, które napisał, przechodzą, a to dowodzi mniej, niż wszyscy by chcieli. Tak naprawdę chcesz otworzyć to w przeglądarce, zanim zbliży się do użytkowników, i to bez proszenia agenta, żeby dotykał produkcji.
Rozwiązują to środowiska podglądu i nie potrzebujesz do nich platformy. Jeden VPS z własnym IPv4, Docker, Caddy i dwa krótkie skrypty dają każdej gałęzi własny URL, na przykład https://feature-login-form.preview.example.com, za hasłem i przebudowywany przy każdym pushu. Agent dostaje klucz SSH, którym może uruchamiać i zatrzymywać podglądy, i nic poza tym.
Zbudowaliśmy to na serwerze testowym i przeszliśmy cały cykl, zanim to opisaliśmy: utworzyć, otworzyć, wypchnąć ponownie, rozebrać, spróbować nadużyć klucza.
Czego potrzebujesz
- VPS z publicznym IPv4. AI-Agent-IP zmieści kilka podglądów naraz.
- Rekordu DNS z wildcardem:
*.preview.example.com→ IP serwera. - Repozytorium aplikacji z plikiem
Dockerfile, który serwuje na porcie 8080. Język dowolny. - Ubuntu 24.04 albo Debiana 12/13 z
apt install -y docker.io caddy git.
Sklonuj repozytorium raz do /srv/app, używając klucza wdrożeniowego tylko do odczytu.
Dwa skrypty
/usr/local/bin/preview-up buduje gałąź i wystawia ją online:
#!/usr/bin/env bash
# preview-up <branch> — build the branch and open it at https://<name>.preview.example.com
set -euo pipefail
BRANCH="$1"
NAME=$(printf '%s' "$BRANCH" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9' '-' | sed 's/--*/-/g; s/^-//; s/-$//' | cut -c1-40)
DIR=/srv/previews/$NAME
git -C /srv/app fetch --quiet origin "$BRANCH"
rm -rf "$DIR"; git -C /srv/app worktree prune
git -C /srv/app worktree add --quiet --force --detach "$DIR" FETCH_HEAD
docker build --quiet -t "preview-$NAME" "$DIR" >/dev/null
docker rm -f "preview-$NAME" >/dev/null 2>&1 || true
docker run -d --name "preview-$NAME" --restart unless-stopped --memory 512m -p 127.0.0.1::8080 "preview-$NAME" >/dev/null
PORT=$(docker port "preview-$NAME" 8080/tcp | head -1 | awk -F: '{print $NF}')
cat > "/etc/caddy/previews/$NAME.caddy" <<CADDY
$NAME.preview.example.com {
import preview_common
reverse_proxy 127.0.0.1:$PORT
}
CADDY
systemctl reload caddy
echo "https://$NAME.preview.example.com"
/usr/local/bin/preview-down ją usuwa:
#!/usr/bin/env bash
# preview-down <branch> — remove the branch preview
set -euo pipefail
NAME=$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9' '-' | sed 's/--*/-/g; s/^-//; s/-$//' | cut -c1-40)
docker rm -f "preview-$NAME" >/dev/null 2>&1 || true
docker image rm "preview-$NAME" >/dev/null 2>&1 || true
rm -f "/etc/caddy/previews/$NAME.caddy"; rm -rf "/srv/previews/$NAME"; git -C /srv/app worktree prune
systemctl reload caddy
Kilka wyborów jest celowych. Kontenery publikują tylko na 127.0.0.1 z losowym portem, więc nic do nich nie dociera inaczej niż przez Caddy. To ważne, bo porty opublikowane przez Dockera omijają ufw. Każdy podgląd ma limit pamięci 512 MB, żeby jedna rozbrykana gałąź nie położyła pozostałych. A ponowne uruchomienie tej samej gałęzi przebudowuje ją na miejscu: push, up, odśwież.
Caddy: jeden plik na podgląd, jedno hasło dla wszystkich
mkdir -p /etc/caddy/previews /srv/previews
chmod +x /usr/local/bin/preview-up /usr/local/bin/preview-down
caddy hash-password --plaintext 'choose-a-review-password'
Wpisz hash do /etc/caddy/Caddyfile:
(preview_common) {
basicauth {
reviewer PASTE-THE-HASH-HERE
}
}
import /etc/caddy/previews/*.caddy
Potem systemctl reload caddy. Każdy podgląd jest teraz za tym samym logowaniem, więc niedokończona gałąź z wyjściem debugowania nie wisi otwarta w internecie. W nowszych wersjach Caddy dyrektywa nazywa się też basic_auth; basicauth nadal działa w obu.
Klucz agenta: up i down, nic więcej
Agent ma wywoływać podglądy, a nie posiadać serwer. Utwórz użytkownika deploy i skrypt bramki, który przyjmuje dokładnie dwa polecenia:
#!/usr/bin/env bash
# /usr/local/bin/preview-gate — the only thing the agent's key may run
set -euo pipefail
read -r ACTION BRANCH EXTRA <<< "${SSH_ORIGINAL_COMMAND:-}"
[[ -z "${EXTRA:-}" && "${BRANCH:-}" =~ ^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$ && "$BRANCH" != *..* ]] || { echo "usage: up|down <branch>" >&2; exit 2; }
case "$ACTION" in
up) exec sudo /usr/local/bin/preview-up "$BRANCH" ;;
down) exec sudo /usr/local/bin/preview-down "$BRANCH" ;;
*) echo "usage: up|down <branch>" >&2; exit 2 ;;
esac
useradd -m -s /bin/bash deploy
echo 'deploy ALL=(root) NOPASSWD: /usr/local/bin/preview-up, /usr/local/bin/preview-down' > /etc/sudoers.d/preview
chmod 440 /etc/sudoers.d/preview && visudo -cf /etc/sudoers.d/preview
chmod 755 /usr/local/bin/preview-gate
W /home/deploy/.ssh/authorized_keys przypnij klucz publiczny agenta do tego skryptu:
restrict,command="/usr/local/bin/preview-gate" ssh-ed25519 AAAA... coding-agent
Z maszyny, na której pracuje agent:
ssh deploy@preview.example.com up feature/login-form
# https://feature-login-form.preview.example.com
ssh deploy@preview.example.com down feature/login-form
W naszym teście up zbudował i opublikował gałąź, drugi up po nowym commicie serwował nową wersję, a down usunął kontener i stronę. Próby z cat /etc/shadow, up x; id, nazwami gałęzi takimi jak -x czy a/../b albo interaktywnym logowaniem tym kluczem dostały tylko usage: up|down <branch>. Nazwa gałęzi musi zaczynać się od litery lub cyfry i nie może zawierać ...
Podpięcie do agenta
W Claude Code lub Codex wystarczy jedna linijka w pliku instrukcji projektu:
Po wypchnięciu gałęzi uruchom
ssh deploy@preview.example.com up <branch>i dodaj wypisany URL do podsumowania. Uruchomdown, gdy gałąź zostanie scalona lub porzucona.
Jeśli Twój CI już uruchamia się przy każdym pushu, wywołaj to samo polecenie stamtąd i trzymaj klucz całkowicie poza środowiskiem agenta. To opcja bardziej rygorystyczna i wybralibyśmy ją do wszystkiego poza projektem prywatnym.
Na co uważać
- Dysk. Każdy build zostawia warstwy obrazów. Cotygodniowy cron z
docker image prune -ftrzyma to w ryzach;downjuż usuwa obraz samego podglądu. - Gałęzie, których nikt nie zamyka. Agenci otwierają więcej gałęzi niż ludzie. Zadanie cron, które wylistuje kontenery
preview-*starsze niż tydzień i uruchomi na nichpreview-down, oszczędzi Ci odkrycia dwudziestu zapomnianych podglądów. - Sekrety. Podglądy powinny działać na danych testowych. Hasło basic auth trzyma obcych z daleka, ale nie sprawia, że można podpiąć podgląd do produkcyjnej bazy danych.
- Czas budowania. Obrazy budują się na serwerze, więc ciężki build konkuruje o CPU z działającymi podglądami. Jeśli buildy trwają minuty, buduj w CI i pobieraj gotowy obraz.
Sam agent nie musi mieszkać na tym serwerze. Jeśli jednak mieszka, zajrzyj do VPS dla Claude Code, a żeby sam zamawiał i doglądał tego serwera, podłącz go do serwera MCP EQVPS. Ta sama maszyna może też hostować bramę MCP Twojego zespołu. Do uruchamiania niezaufanego kodu bez żadnego serwera są sandboxy, a plany z własnym IPv4 zaczynają się od $8 miesięcznie.
Komentarze
Brak komentarzy. Bądź pierwszy.