Wakala wa kuandika msimbo anasema kipengele kinafanya kazi. Labda ni kweli. Majaribio aliyoandika yanapita, na hilo linathibitisha machache kuliko tungependa sote. Unachotaka hasa ni kukifungua kwenye kivinjari kabla hakijakaribia watumiaji, na kufanya hivyo bila kumwomba wakala aguse uzalishaji.
Mazingira ya onyesho la awali hutatua hilo, na huhitaji jukwaa kwa ajili yake. VPS moja yenye IPv4 yake, Docker, Caddy na hati mbili fupi hupa kila tawi URL yake kama https://feature-login-form.preview.example.com, nyuma ya nenosiri, likijengwa upya kila push. Wakala hupata ufunguo wa SSH unaoweza kuwasha na kuzima maonyesho ya awali na si zaidi.
Tulijenga hili kwenye seva ya majaribio na kupitia mzunguko mzima kabla ya kuliandika: kuunda, kufungua, push tena, kuondoa, na kujaribu kutumia ufunguo vibaya.
Unachohitaji
- VPS yenye IPv4 ya umma. AI-Agent-IP ina nafasi kwa maonyesho kadhaa kwa wakati mmoja.
- Rekodi ya DNS ya wildcard:
*.preview.example.com→ IP ya seva. - Hazina ya programu yako yenye
Dockerfileinayohudumia kwenye mlango 8080. Lugha yoyote inafaa. - Ubuntu 24.04 au Debian 12/13 pamoja na
apt install -y docker.io caddy git.
Nakili hazina mara moja kwenda /srv/app kwa ufunguo wa deploy wa kusoma tu.
Hati mbili
/usr/local/bin/preview-up hujenga tawi na kuliweka mtandaoni:
#!/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 huliondoa:
#!/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
Baadhi ya machaguo humo ni ya makusudi. Kontena huchapishwa kwenye 127.0.0.1 tu kwa mlango wa nasibu, kwa hiyo hakuna kinachoyafikia isipokuwa kupitia Caddy. Hilo ni muhimu kwa sababu milango inayochapishwa na Docker hupita ufw. Kila onyesho la awali lina kikomo cha kumbukumbu cha 512 MB, ili tawi moja lisilodhibitiwa lisiyaangushe mengine. Na kuendesha tawi lile lile tena hulijenga upya pale pale: push, endesha up, onyesha upya.
Caddy: faili moja kwa kila onyesho, nenosiri moja kwa yote
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'
Weka hash kwenye /etc/caddy/Caddyfile:
(preview_common) {
basicauth {
reviewer PASTE-THE-HASH-HERE
}
}
import /etc/caddy/previews/*.caddy
Kisha systemctl reload caddy. Sasa kila onyesho la awali liko nyuma ya kuingia kule kule, kwa hiyo tawi lililokamilika nusu lenye matokeo ya utatuzi halibaki wazi kwenye intaneti. Katika matoleo mapya ya Caddy agizo hili pia huandikwa basic_auth; basicauth bado hufanya kazi kwenye yote mawili.
Ufunguo wa wakala: up na down, si zaidi
Wakala anahitaji kuwasha maonyesho ya awali, si kumiliki seva. Unda mtumiaji deploy na hati ya lango inayokubali amri mbili hasa:
#!/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
Kwenye /home/deploy/.ssh/authorized_keys, funga ufunguo wa umma wa wakala kwenye hati hiyo:
restrict,command="/usr/local/bin/preview-gate" ssh-ed25519 AAAA... coding-agent
Kutoka kwa mashine anayofanyia kazi wakala:
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
Kwenye jaribio letu, up ilijenga na kuchapisha tawi, up ya pili baada ya commit mpya ilitoa toleo jipya, na down iliondoa kontena na tovuti. Majaribio ya cat /etc/shadow, up x; id, majina ya matawi kama -x au a/../b, au kuingia kwa mwingiliano kwa ufunguo huo yote yalipata usage: up|down <branch> tu. Jina la tawi lazima lianze kwa herufi au tarakimu na haliwezi kuwa na ...
Kuliunganisha na wakala
Kwa Claude Code au Codex, mstari mmoja kwenye faili ya maelekezo ya mradi unatosha:
Baada ya kusukuma tawi, endesha
ssh deploy@preview.example.com up <branch>na uweke URL iliyochapishwa kwenye muhtasari wako. Endeshadowntawi likiunganishwa au likiachwa.
Ikiwa CI yako tayari huendeshwa kila push, ita amri hiyo hiyo kutoka huko na uweke ufunguo mbali kabisa na mazingira ya wakala. Hilo ni chaguo kali zaidi, na tungelichagua kwa chochote kinachozidi mradi binafsi.
Mambo ya kufuatilia
- Diski. Kila ujenzi huacha tabaka za picha. Cron ya kila wiki yenye
docker image prune -fhuidhibiti;downtayari hufuta picha ya onyesho lenyewe. - Matawi ambayo hakuna anayeyafunga. Mawakala hufungua matawi mengi kuliko watu. Kazi ya cron inayoorodhesha kontena za
preview-*zilizo na zaidi ya wiki moja na kuendeshapreview-downjuu yake inakuepusha kukuta maonyesho ishirini yaliyosahauliwa. - Siri. Maonyesho ya awali yanapaswa kuendeshwa kwa vitambulisho vya majaribio. Nenosiri la basic auth huwazuia wageni, lakini haifanyi iwe sawa kuunganisha onyesho na hifadhidata ya uzalishaji.
- Muda wa ujenzi. Picha hujengwa kwenye seva, kwa hiyo ujenzi mzito hushindana kwa CPU na maonyesho yanayoendeshwa. Ujenzi ukichukua dakika, jenga kwenye CI na uvute picha.
Wakala mwenyewe hahitaji kuishi kwenye seva hii. Akiishi, ona VPS kwa Claude Code, na ili aagize na kuitunza seva hiyo mwenyewe, muunganishe na seva ya MCP ya EQVPS. Mashine hiyo hiyo pia inaweza kupangisha lango la MCP la timu yako. Kwa kuendesha msimbo usioaminika bila seva yoyote kuna sandbox, na mipango yenye IPv4 yake huanzia $8 kwa mwezi.
Maoni
Bado hakuna maoni. Kuwa wa kwanza.