backend-go memakai dua workflow GitHub Actions untuk deploy — satu untuk staging, satu untuk produksi. Keduanya berjalan di self-hosted runner (bukan GitHub-hosted), dan memakai pola git reset --hard + rebuild + restart service.
Overview
| Workflow | File | Trigger | Runner | Target |
|---|---|---|---|---|
| Staging | .github/workflows/deploy_staging.yml | push ke staging | [self-hosted, staging] | Staging |
| Produksi | .github/workflows/deploy_production.yml | push ke v3 | [self-hosted, production, api] + [self-hosted, production, cron] | Produksi (2 host) |
Self-hosted runner
Runner bukan GitHub-hosted (ubuntu-latest), tapi self-hosted yang terdaftar di server Dazo. Runner ini punya akses sudo untuk:
systemctl restart— restart systemd service (Go API)pm2 restart/pm2 start— restart Node engine workersgit fetch+git reset --hard— pull code ke server
Label runner
| Label | Workflow | Host |
|---|---|---|
[self-hosted, staging] | deploy_staging.yml | Server staging (Go API + Node engine satu mesin) |
[self-hosted, production, api] | deploy_production.yml deploy_api job | Host API produksi (Go binary) |
[self-hosted, production, cron] | deploy_production.yml deploy_cron job | Host cron produksi (Node engine) |
Variabel GitHub (repo Settings → Secrets and variables → Actions)
Workflow memakai repository variables (bukan secrets) untuk path:
| Variable | Dipakai di | Contoh nilai | Catatan |
|---|---|---|---|
vars.PROJECT_DIR_DEVELOPMENT | staging | /opt/dazo-dev | Path repo di server staging |
vars.PROJECT_DIR_PRODUCTION | produksi (api) | /opt/dazo | Path repo di host API |
vars.PROJECT_DIR_CRON_PRODUCTION | produksi (cron) | /opt/dazo-cron | Path repo di host cron |
vars.SERVICE_NAME_DEV | staging | "cron_mdv2 cron_botV2 cron_broadcast" | Daftar PM2 service names (space-separated) |
vars.SERVICE_NAME_PROD | produksi (cron) | "cron_mdv2 cron_botV2 cron_broadcast" | Daftar PM2 service names produksi |
Workflow 1 — Staging (deploy_staging.yml)
Trigger: push ke staging branch. Satu job deploy.
Langkah-langkah
1. Checkout repository
└─ actions/checkout@v3
2. Set PATH for Go
└─ echo "PATH=$PATH:/usr/local/go/bin" >> $GITHUB_ENV
3. Build Go application
├─ cd $PROJECT_DIR_DEVELOPMENT
├─ git fetch --all
├─ git reset --hard origin/staging # DESTRUCTIVE — perubahan server hilang
├─ go mod tidy
├─ go build -o dazodev main.go # binary: dazodev
└─ cd engine/ && npm i # Node engine install
4. Check & Restart Service DazoDev
├─ if systemctl is-active --quiet dazodev:
│ └─ systemctl restart dazodev
└─ else: systemctl start dazodev
└─ systemctl status dazodev --no-pager
5. Check & Restart Service Engine
├─ cron=($SERVICE_NAME_DEV) # array dari vars
└─ for cron_name in "${cron[@]}":
├─ if pm2 describe "$cron_name":
│ └─ pm2 restart "$cron_name"
└─ else: pm2 start "$PROJECT_DIR_PRODUCTION/engine/$cron_name.js" --name "$cron_name"Karakteristik staging
- Satu mesin — Go API + Node engine di server yang sama
- Binary:
dazodev(bukandazo) - Service name:
dazodev(systemd) - Tidak ada deploy untuk
engine/terpisah —npm ijalan inline
Workflow 2 — Produksi (deploy_production.yml)
Trigger: push ke v3 branch. Dua job paralel di host berbeda.
Job 1: deploy_api — host [self-hosted, production, api]
1. Checkout repository
└─ actions/checkout@v3
2. Set PATH for Go
└─ echo "PATH=$PATH:/usr/local/go/bin" >> $GITHUB_ENV
3. Build Go application
├─ cd $PROJECT_DIR_PRODUCTION
├─ git fetch --all
├─ git reset --hard origin/v3 # DESTRUCTIVE
├─ go mod tidy
└─ go build -o dazo main.go # binary: dazo
4. Check & Restart Service Dazo
├─ if systemctl is-active --quiet dazo:
│ └─ systemctl restart dazo
└─ else: systemctl start dazo
└─ systemctl status dazo --no-pagerJob 2: deploy_cron — host [self-hosted, production, cron]
1. Fetch Latest Repository
├─ cd $PROJECT_DIR_CRON_PRODUCTION
├─ git fetch --all
└─ git reset --hard origin/v3 # DESTRUCTIVE — tidak ada go build di host ini
2. Check & Restart Service Engine
├─ cron=($SERVICE_NAME_PROD)
└─ for cron_name in "${cron[@]}":
├─ if pm2 describe "$cron_name":
│ └─ pm2 restart "$cron_name"
└─ else: pm2 start "$PROJECT_DIR_PRODUCTION/engine/$cron_name.js" --name "$cron_name"Perbandingan staging vs produksi
| Aspek | Staging | Produksi (api) | Produksi (cron) |
|---|---|---|---|
| Trigger branch | staging | v3 | v3 |
| Runner label | [self-hosted, staging] | [self-hosted, production, api] | [self-hosted, production, cron] |
git fetch + reset --hard | ✅ | ✅ | ✅ |
go build | ✅ → dazodev | ✅ → dazo | ❌ |
npm i (engine) | ✅ | ❌ | ❌ |
| Restart systemd | ✅ dazodev | ✅ dazo | ❌ |
| Restart PM2 | ✅ | ❌ | ✅ |
| Jumlah job | 1 | 2 (paralel) | 2 (paralel) |
systemd service
Go API dikelola via systemd:
| Environment | Service name | Binary | Path repo |
|---|---|---|---|
| Staging | dazodev | dazodev | $PROJECT_DIR_DEVELOPMENT |
| Produksi | dazo | dazo | $PROJECT_DIR_PRODUCTION |
Pola restart (digunakan di workflow)
if sudo systemctl is-active --quiet dazo; then
sudo systemctl restart dazo
else
sudo systemctl start dazo
fi
sudo systemctl status dazo --no-pagerLogik: jika service sudah running, restart. Jika belum (server baru / setelah crash), start. Tidak ada enable — service tidak auto-start saat boot kecuali di-setup terpisah.
PM2 service (Node engine)
Node engine dikelola via PM2. Workflow loop melalui daftar service names dari vars.SERVICE_NAME_*:
cron=($SERVICE_NAME_PROD) # array dari GitHub vars
for cron_name in "${cron[@]}"; do
if sudo pm2 describe "$cron_name" > /dev/null 2>&1; then
sudo pm2 restart "$cron_name"
else
sudo pm2 start "$PROJECT_DIR_PRODUCTION/engine/$cron_name.js" --name "$cron_name"
fi
doneService names (contoh, dari vars.SERVICE_NAME_PROD)
| Service | File | Fungsi |
|---|---|---|
cron_mdv2 | engine/cron_mdv2.js | Outbox polling → WA gateway :5002 (legacy devices) |
cron_botV2 | engine/cron_botV2.js | Chatbot automation |
cron_broadcast | engine/cron_broadcast.js | Broadcast scheduler/executor |
| (lainnya) | engine/cron_*.js | Worker lain |
Yang tidak ada (gap)
Setup server baru
Untuk menjalankan workflow di server baru, yang harus tersedia:
- Self-hosted runner terdaftar dengan label yang sesuai (
staging,production,api,production,cron) - Go terinstall di
/usr/local/go/bin(lihatSet PATH for Gostep) - systemd unit —
dazo.service(produksi) /dazodev.service(staging). Tidak ada template di repo. - PM2 terinstall + semua
cron_*.jssudah pernah di-start manual sekali (hindari bug #27) - Repo sudah ter-clone di path yang sesuai (
$PROJECT_DIR_*) handlers/environtment.go— gitignored, harus dibuat manual dengan konstanta yang sesuai environment. Lihat Environment..env—GO_ENV=production+ kredensial.JWT_SECRETharus sama dengandazoapp.- Node.js terinstall untuk
npm idi engine - MongoDB dapat diakses dari host API
- Redis dapat diakses dari host cron (untuk BullMQ)
Monitoring deploy
Cek status deploy:
- GitHub repo → Actions tab → lihat workflow run
- Di server:
systemctl status dazo/pm2 status - Log:
journalctl -u dazo -f(systemd) /pm2 logs(Node engine) - Health:
curl http://localhost:8081/— harus balas{"Dazo":"App"}
Langkah berikutnya
- Deployment — checklist sebelum push, alur operasi git
- Environment —
.env+handlers/environtment.go - Local Development — cara jalankan lokal
- Tech Debt — bug #27 (PM2 first-start)