Deploy Afresto Next
Tumpukan: backend Go (push→GHCR→Watchtower ~5 mnt) · web React (Vite→Cloudflare Worker,
manual wrangler deploy) · mobile RN/Expo di repo terpisah <repo-mobile>
(EAS OTA). Deploy produksi = selalu minta izin user dulu (mereka menyetujui tiap kali).
🔴 Aturan #1 — repo ini sering dipakai BANYAK TAB AGENT sekaligus
git status kerap menampilkan berkas termodifikasi milik sesi lain. Karena itu:
- JANGAN
git add -a/git commit -a/git add .— akan menyapu pekerjaan setengah jadi sesi lain masuk commit-mu. - Stage SELEKTIF tiap berkas milikmu, lalu verifikasi:
git diff --cached --name-only— pastikan tak ada berkas asing (yang sering nyasar:web/src/pages/ChatPage.tsx,backend/internal/db/dbgen/models.gobila bukan hasil regen-mu). - Berkas dipakai bersama dua sesi (mis. ExamPage) & kamu hanya mau hunk-mu: jangan
stash/reset(menarik berkas dari bawah kaki agent lain). Sunting index Git lewat blob:git show :path > tmp→ buang hunk mereka daritmp→SHA=$(git hash-object -w tmp)→git update-index --cacheinfo 100644,$SHA,path. Berkas di disk tak tersentuh. - 🚨 cwd tool Bash bisa MENETAP dari
cd .../web(atau worktree) di panggilan sebelumnya. Lalugit add backend/...GAGALpathspec did not match(atau MEN-stage tree yang salah). Sebelum stage selektif,cd <repo-root> &&dulu (atau pakai path absolut). Verifikasigit diff --cached --name-onlymenampilkan path relatif-root yang benar (bukan../backend,src/...). - Setelah deploy: cek
git status— pastikan berkas kerja sesi lain masih utuh.
Gerbang mutu Go (sebelum commit backend)
Padanan Pint + PHPStan ala Laravel untuk Go. Jalankan lokal sebelum stage berkas backend:
- Format:
cd backend && gofmt -l .→ harus KOSONG. Ada isi →gofmt -w .(auto-fix), stage hasilnya. (Padananpint --test.) - Build + vet:
go build ./... && go vet ./...hijau. - Analisis statis (target):
golangci-lint run ./...(bungkus gofmt/govet/staticcheck/errcheck/ineffassign/unused/misspell). Padananphpstan analyse. - ⚠️ Status: gerbang CI belum aktif. Belum ada
backend/.golangci.ymlmaupun.github/workflows/ci.yml, danstaticcheckmasih usang → jadi format+build+vet ini manual dulu. Rencana enforce di CI + config lengkap:docs/plan/00-tooling-quality-gate.md. Kode generatedinternal/db/dbgendikecualikan dari lint. - Test menyertai perubahan → skill afresto-testing (
go test ./...; integration ter-skip tanpaTEST_DATABASE_URL).
🔴 Aturan #00 — pekerjaan BERMAKNA masuk GitHub issue DULU (sejak 5 Sep 2026)
Berlaku sebelum Aturan #0 (cabang + PR): sebelum menyentuh kode sama sekali.
Tujuannya bukan formalitas. Permintaan yang datang lewat obrolan hanya diketahui dua pihak — tim tak bisa membacanya, pemilik tak punya tempat menugaskan, dan modulnya tak meninggalkan jejak siapa mengerjakan apa.
Kapan WAJIB issue
Fitur baru · perubahan yang dilihat sekolah · perubahan skema DB · apa pun yang butuh keputusan produk · pekerjaan yang lebih dari satu PR.
Kapan TIDAK perlu
Bug yang jelas & sempit · koreksi teks/label · dokumentasi · tindak lanjut langsung dari komentar review PR.
Ragu → buat issue. Murah, dan bisa ditutup. Ambang ini yang menjaga aturannya hidup: kalau setiap permintaan sepele jadi issue, orang akan mulai melewatinya diam-diam, dan sekali dilewati tanpa akibat, aturannya mati.
Yang dilakukan agen
- Telusuri kode secukupnya untuk mengisi "temuan awal" & menemukan blocker — jangan menebak.
- Buat issue pakai template
.github/ISSUE_TEMPLATE/pekerjaan.md, beri labelmodul:*(itu yang membentuk catatan per modul), plusbutuh-keputusanbila ada yang menggantung. - BERHENTI. Jangan koding, jangan buat cabang, jangan menugaskan diri sendiri. Penugasan adalah tindakan pemilik; tim juga perlu ruang memberi masukan/merevisi ide.
- Laporkan nomor issue-nya ke pemilik, beserta keputusan yang masih menggantung (bila ada).
🔑 Pemilik boleh melewatinya dengan bilang "kerjakan sekarang" — issue tetap dibuat sebagai catatan, pekerjaan jalan terus. Aturan ini mengatur bawaan, bukan memenjarakan.
Blocker yang baru ketahuan DI TENGAH pengerjaan
Beri tahu sebelum melanjutkan: komentar di issue (supaya tim ikut melihat) dan sampaikan
ke pemilik. Bila keputusannya menghentikan pekerjaan → tambah label butuh-keputusan.
Jangan diam-diam memilih tafsir sendiri lalu jalan terus — itu yang menghasilkan pekerjaan yang
"selesai" tapi salah.
Menutup lingkarannya
PR wajib memuat Closes #<nomor> di badannya. Itu yang menyambungkan issue → PR → orang,
sehingga gh issue list --label modul:ujian --state all menjadi catatan siapa mengerjakan apa
di modul itu. Tanpa baris itu, jejaknya putus.
🔴 Aturan #0 — JANGAN commit/push langsung ke main (tim 4 orang, sejak 25 Jul 2026)
Semua kerja lewat cabang + Pull Request; hanya reviewer yang merge ke main (merge = pemicu deploy).
- Sebelum mulai:
git checkout main && git pull— bila ada migrasi baru, jalankan migrasi dulu. - Buat/checkout cabang bertema SEBELUM menyentuh kode:
- fitur →
feat/<nama>· bug →bugfix/<nama>· bug ber-issue → cabang baru + pelajari issue-nya.
- fitur →
- Commit selektif →
git push -u origin <cabang>(BUKAN ke main). - Buka PR ke
main:gh pr createbilaghlogin; kalau belum, beri user URLhttps://github.com/Afresto-Next/next/compare/main...<cabang>?expand=1. - Reviewer approve & merge → deploy. JANGAN merge PR sendiri.
- Bagian "Push backend / Deploy WEB" di bawah = mekanik yang jalan SETELAH PR merge ke main (biasanya oleh yang bertugas deploy), bukan izin push langsung ke main.
- Perubahan skill pun lewat PR (CODEOWNERS).
Urutan langkah
1. Commit (Bash tool = Git Bash / sh, BUKAN PowerShell)
- Pesan multi-baris: heredoc
git commit -q -F - <<'EOF' … EOF. JANGAN@'…'@(itu here-string PowerShell → subjek tercemar jadi@ ...). - Akhiri pesan:
Co-Authored-By: Claude Opus 4.8 <[email protected]>. - Warning "CRLF will be replaced by LF" = normal, abaikan.
- Commit ke cabang tugasmu (
feat/*·bugfix/*), bukanmain(lihat Aturan #0). User commit saat diminta.
🔴 JANGAN git add -A / git add <dir>/ — sudah 3x menyapu berkas user
Repo ini menyimpan banyak berkas kerja user yang belum terlacak: docs/bskap/ (gambar+PDF),
docs/analisis butir soal/, elibrary/, cloudflare/**/.wrangler/ (cache akun). Sekali -A,
semuanya ikut.
Kejadian nyata: git add docs/ menyeret PDF/xls/gambar BSKAP (25 Jul & 2 Agu); git add -A
menyeret 446 berkas termasuk wrangler-account.json (9 Agu).
git add path/ke/berkas1 path/ke/berkas2 # SATU PER SATU, selalu
git commit -q -F - <<'EOF'
...
EOF
git show --name-only --format="" HEAD | wc -l # cocok dgn yang kamu niatkan?
Kalau jumlahnya melenceng: git reset --soft HEAD~1 && git reset, stage ulang yang benar.
Bila sudah ter-push ke cabang sendiri, git push --force-with-lease aman.
2. Migrasi DB — WAJIB di commit TIP
.github/workflows/migrate.yml mendeteksi migrasi via git diff --name-only HEAD~1 HEAD (hanya commit teratas), berjalan setelah "Build & Push Docker Images" sukses (workflow_run).
- Bila push berisi migrasi → migrasi harus ada di commit TIP (satu commit berisi migrasi+kode = aman).
- Migrasi jalan otomatis di self-hosted runner VM setelah build.
run --rm migratesinkron → job GAGAL keras bila error (bukan senyap). - Verifikasi migrasi: cek tab Actions → "Migrate DB" hijau untuk commit itu (
ghCLI tersedia:gh run list --workflow="Migrate DB (self-hosted)" --limit 1— agen bisa cek sendiri, tak perlu menyuruh user). Watchtower tukar imageapidi poll berikutnya (~5 mnt), hampir selalu setelah migrasi selesai. - SEBELUM push migrasi, verifikasi SQL-nya via psql
BEGIN; … ROLLBACK;di DB lokal (v102 = prod). Lihat skillafresto-db-change/ jebakan-rekayasa §4.
🔴 Nomor migrasi: periksa ulang TEPAT SEBELUM MERGE, bukan saat membuat cabang
Rekan tim bisa men-merge seri migrasi lain selagi cabangmu terbuka. Nomor kembar membuat
goose PANIK di sortAndConnectMigrations — bukan galat SQL, jadi mudah salah dibaca.
✅ Tak ada kerusakan data: panik terjadi saat mengumpulkan daftar, sebelum satu pun dijalankan.
Kejadian nyata (9 Agu 2026): dipilih 00138 saat membuat cabang; saat merge, tim sudah memakai
00138–00140. Perbaikan pertama menamainya 00140 dan masih bentrok — karena menebak lagi.
Hitung, jangan tebak:
git pull # WAJIB dulu, biar lihat migrasi rekan
MAX=$(ls backend/migrations/ | sed 's/_.*//' | sort -n | tail -1)
NEXT=$(printf "%05d" $((10#$MAX + 1)))
ls backend/migrations/ | sed 's/_.*//' | sort | uniq -d # HARUS KOSONG
Kalau sudah terlanjur: git mv ke nomor baru, PR kecil, merge, lalu jalankan ulang workflow
"Migrate DB (self-hosted)" dari tab Actions.
3. Push backend (SETELAH PR merge ke main — lihat Aturan #0)
Merge PR → main diperbarui → GHCR → Watchtower ~5 mnt menukar image. Tak ada perubahan backend = tak perlu tunggu Watchtower. (Saat mengembangkan: git push -u origin <cabang> lalu PR — jangan push main.)
⚠️ SETELAH merge backend: CEK 502 (sudah kambuh 6x)
Membuat-ulang container api/worker sering memicu outage total — semua container Up tapi
seluruh rute 502. Praktis tiap merge backend adalah lemparan dadu.
curl -s -o /dev/null -w "%{http_code}
" --max-time 15 https://next.afresto.co/api/v1/auth/me
# 401 = SEHAT (minta autentikasi) · 502 = outage
Bila 502 → Actions → "Ops — pulihkan API" → restart-docker (tanpa SSH, pulih <1 menit).
Jangan membedah fitur yang baru di-deploy: 502 + container Up = restart daemon Docker.
Detail: docs/ops-auto-pulih.md.
4. Deploy WEB — dari WORKTREE BERSIH (karena banyak tab agent)
npm run build membaca SELURUH working tree → pekerjaan setengah jadi sesi lain ikut terbit. Jadi build & deploy dari worktree di commit-mu:
rm -rf /c/wtd 2>/dev/null; git worktree add --detach /c/wtd <SHA-commit-mu>
cd /c/wtd && git status --short # harus KOSONG (bersih)
cd /c/wtd/web && npm ci && npm run build
cd /c/wtd/cloudflare/web-worker && npx wrangler deploy
cd <repo-projectTwo> && git worktree remove --force /c/wtd && git worktree prune
- 🔥 Path worktree WAJIB PENDEK (
/c/wtd) — scratchpad dalam + nama PDF kaldik DKI yang panjang → error Windows "Filename too long" saat checkout. - Verifikasi bundel benar bila perlu:
grep -rl "<penanda fiturmu>" /c/wtd/web/dist/assets/*.js— tapi hati-hati positif palsu (penanda umum bisa dari fitur lain). - Cloudflare cache aset ~20 dtk → uji dengan Ctrl+F5. (200 tapi ukuran ~397 byte = fallback, bukan aset asli.)
5. Mobile OTA — repo terpisah, --environment WAJIB
cd "<repo-mobile>"
npx eas update --branch preview --environment preview --message "…" --non-interactive
- 🔥
--environmentWAJIB (preview/production) — tanpa itu update salah environment/gagal. - Repo mobile punya remote GitHub (
Afresto-Next/afresto-next-mobile) → berlaku Aturan #0: cabang → PR → merge, jangan push langsung kemain. - OTA hanya JS/aset. Perubahan modul native → CRASH bila via OTA → wajib build ulang.
🔥 eas update mengirim WORKING TREE, bukan commit
Sama persis dengan npm run build di langkah 4 — seluruh direktori kerja ikut terbundel,
termasuk pekerjaan setengah jadi sesi lain. Tanda * pada baris Commit <hash>* di hasil
publikasi = tree kotor.
WAJIB tepat sebelum publish — cetak, lalu cocokkan dengan baris Commit di hasil:
echo "publish dari: $(git branch --show-current) @ $(git log --oneline -1 --format=%h)"
git log --oneline HEAD..origin/main # KOSONG = tak ada pekerjaan orang lain yang tertinggal
- Publikasikan dari
main, bukan cabang fitur. Dari cabang, pekerjaan orang lain yang sudah merge HILANG dari channel — OTA mengganti seluruh bundel, tidak menambal. - Produksi: pinggirkan dulu perubahan tak-commit milik sesi lain
(
git stash push -- <berkas>…git stash pop). Produksi tak boleh membawa kode yang belum ditinjau.
🚨 Cabang bisa BERPINDAH di tengah pekerjaan (banyak tab agent, satu klon)
Kejadian 20 Agu 2026: publikasi preview jelas dari main @ d0938fa; perintah berikutnya
mendapati HEAD sudah di cabang lain @ 8126a50 — sesi lain berpindah cabang di klon yang sama.
Produksi sempat menjalankan kode 3 PR lebih lama, tanpa satu pun error.
➡️ Jangan berasumsi cabang masih sama antar-perintah. Cetak ulang tiap kali (perintah di atas). ➡️ Pencegahan sesungguhnya: OTA dari WORKTREE, sama seperti deploy web di langkah 4 — sesi lain tak bisa menggeser cabangnya:
rm -rf /c/wtm 2>/dev/null; git worktree add --detach /c/wtm <SHA-commit-mu>
cd /c/wtm && git status --short # harus KOSONG
npm ci && npx eas update --branch <ch> --environment <env> --message "…" --non-interactive
cd <repo-mobile> && git worktree remove --force /c/wtm && git worktree prune
🪤 Path worktree pendek (/c/wtm) — alasan sama dengan langkah 4.
🪤 npm ci di worktree perlu waktu; untuk OTA kecil boleh tetap di klon utama asal verifikasi
cabang+commit di atas dijalankan dan hasilnya cocok.
🏢 GHCR & organisasi GitHub — repo kini di org Afresto-Next
Repo (next, afresto-next-mobile, afresto-claude-skills) pindah dari ristology/* → org Afresto-Next (20 Jul 2026). Runbook transfer lengkap: docs/runbook-transfer-next-org.md.
- 🚨 Transfer repo ke org = path GHCR ikut owner → Watchtower BEKU DIAM-DIAM. CI tag image
ghcr.io/${GITHUB_REPOSITORY_OWNER}/afresto-next-{api,web,migrate}— owner mengikuti pemilik repo. Setelah transfer, image baru masukghcr.io/afresto-next/*TAPI VMdocker-compose.prod.ymlmasih pullghcr.io/${GHCR_OWNER:-ristology}/*→ produksi tak update (image lama tetap jalan, TANPA error). Package GHCR TIDAK ikut pindah (tetap milik akun lama; org bikin package BARU yang private). FIX di VM (~/afresto-next): setGHCR_OWNER=afresto-nextdi.env→docker login ghcr.io(PATread:packages) →pull api web migrate→up -d→restart watchtower(watchtower bind~/.docker/config.json; tanpa restart pakai creds lama → auto-update mati senyap). Runner self-hosted + variabel ActionsDEPLOY_DIRIKUT pindah otomatis (tak perlu daftar ulang; tetap verifikasi Settings→Actions→Runners online). Rollback: balikGHCR_OWNERlama + pull/up. 🚫 JANGAN hapus packageghcr.io/ristology/*lama sampai path baru stabil beberapa siklus. - 🪤
docker compose pull(tanpa argumen) GAGAL untuk image:local(caddy/backup dibangun DI VM, bukan GHCR) → "pull access denied … afresto-next-caddy" — WAJAR, abaikan. Pull selektifpull api web migrate.up -dtak menariknya (image lokal sudah ada).
Verifikasi tanpa akses langsung (probe dari luar)
- Rute baru ada?
curl -o /dev/null -w '%{http_code}' https://afresto.co/api/v1/<rute>→ 401 = terdaftar (backend baru naik), 404 = belum. Host benar:afresto.co/origin.afresto.co+ prefix/api/v1.api.afresto.coTIDAK ADA (selalu 404). - Migrasi
schoolssehat?GET https://afresto.co/api/v1/public/branding?subdomain=demo→ 200 =SELECT * schoolssehat. - Perubahan PERILAKU (bukan rute baru) tak bisa di-probe → jangan klaim "terverifikasi", sebut tunggu Watchtower / uji di
next.afresto.co(demo, bebas dicoba).
🔥 Checklist ringkas sebelum bilang "selesai"
- Backend?
gofmt -l backendkosong +go build/vethijau + test menyertai (afresto-testing). git status→ hanya berkasku ter-stage; sesi lain utuh.- Migrasi (bila ada) di commit TIP; Actions "Migrate DB" hijau.
- Web dari worktree bersih
/c/wtd; worktree dibersihkan setelahnya. - OTA pakai
--environment, darimain, dan cabang+commit dicetak lalu dicocokkan dengan barisCommitdi hasil publikasi (*= tree kotor, kerja sesi lain ikut terkirim). - Backend butuh Watchtower ~5 mnt sebelum diuji; web cukup Ctrl+F5.
- Sentuh auth/nilai? Pertimbangkan
/code-reviewpada diff dulu.
Terkait: afresto-db-change · afresto-code-style (arsitektur berlapis) · afresto-testing · docs/plan/00 (gerbang mutu) · memori afresto-next-jebakan-rekayasa (§11b migrate.yml, §11 heredoc), afresto-next-progress-2026-07-17-sore (pola worktree), afresto-next-gcp-vm-deploy, afresto-next-frontend-cloudflare-worker.