D
Setup

CI/CD & GitHub Actions

Dua workflow GitHub Actions — staging (push ke staging branch, single job) dan produksi (push ke v3 branch, dua job paralel di host terpisah). Self-hosted runner, systemd + PM2.

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

WorkflowFileTriggerRunnerTarget
Staging.github/workflows/deploy_staging.ymlpush ke staging[self-hosted, staging]Staging
Produksi.github/workflows/deploy_production.ymlpush 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 workers
  • git fetch + git reset --hard — pull code ke server

Label runner

LabelWorkflowHost
[self-hosted, staging]deploy_staging.ymlServer staging (Go API + Node engine satu mesin)
[self-hosted, production, api]deploy_production.yml deploy_api jobHost API produksi (Go binary)
[self-hosted, production, cron]deploy_production.yml deploy_cron jobHost cron produksi (Node engine)

Variabel GitHub (repo Settings → Secrets and variables → Actions)

Workflow memakai repository variables (bukan secrets) untuk path:

VariableDipakai diContoh nilaiCatatan
vars.PROJECT_DIR_DEVELOPMENTstaging/opt/dazo-devPath repo di server staging
vars.PROJECT_DIR_PRODUCTIONproduksi (api)/opt/dazoPath repo di host API
vars.PROJECT_DIR_CRON_PRODUCTIONproduksi (cron)/opt/dazo-cronPath repo di host cron
vars.SERVICE_NAME_DEVstaging"cron_mdv2 cron_botV2 cron_broadcast"Daftar PM2 service names (space-separated)
vars.SERVICE_NAME_PRODproduksi (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

text
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 (bukan dazo)
  • Service name: dazodev (systemd)
  • Tidak ada deploy untuk engine/ terpisah — npm i jalan 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]

text
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-pager

Job 2: deploy_cron — host [self-hosted, production, cron]

text
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

AspekStagingProduksi (api)Produksi (cron)
Trigger branchstagingv3v3
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 job12 (paralel)2 (paralel)

systemd service

Go API dikelola via systemd:

EnvironmentService nameBinaryPath repo
Stagingdazodevdazodev$PROJECT_DIR_DEVELOPMENT
Produksidazodazo$PROJECT_DIR_PRODUCTION

Pola restart (digunakan di workflow)

bash
if sudo systemctl is-active --quiet dazo; then
    sudo systemctl restart dazo
else
    sudo systemctl start dazo
fi
sudo systemctl status dazo --no-pager

Logik: 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_*:

bash
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
done

Service names (contoh, dari vars.SERVICE_NAME_PROD)

ServiceFileFungsi
cron_mdv2engine/cron_mdv2.jsOutbox polling → WA gateway :5002 (legacy devices)
cron_botV2engine/cron_botV2.jsChatbot automation
cron_broadcastengine/cron_broadcast.jsBroadcast scheduler/executor
(lainnya)engine/cron_*.jsWorker lain

Yang tidak ada (gap)

Setup server baru

Untuk menjalankan workflow di server baru, yang harus tersedia:

  1. Self-hosted runner terdaftar dengan label yang sesuai (staging, production,api, production,cron)
  2. Go terinstall di /usr/local/go/bin (lihat Set PATH for Go step)
  3. systemd unit — dazo.service (produksi) / dazodev.service (staging). Tidak ada template di repo.
  4. PM2 terinstall + semua cron_*.js sudah pernah di-start manual sekali (hindari bug #27)
  5. Repo sudah ter-clone di path yang sesuai ($PROJECT_DIR_*)
  6. handlers/environtment.go — gitignored, harus dibuat manual dengan konstanta yang sesuai environment. Lihat Environment.
  7. .env — GO_ENV=production + kredensial. JWT_SECRET harus sama dengan dazoapp.
  8. Node.js terinstall untuk npm i di engine
  9. MongoDB dapat diakses dari host API
  10. Redis dapat diakses dari host cron (untuk BullMQ)

Monitoring deploy

Cek status deploy:

  1. GitHub repo → Actions tab → lihat workflow run
  2. Di server: systemctl status dazo / pm2 status
  3. Log: journalctl -u dazo -f (systemd) / pm2 logs (Node engine)
  4. Health: curl http://localhost:8081/ — harus balas {"Dazo":"App"}

Langkah berikutnya