> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sertifikasitrainer.com/llms.txt
> Use this file to discover all available pages before exploring further.

# CI/CD GitHub Actions

> Pipeline CI otomatis dan deploy manual ke Cloudflare production.

Repo memakai **dua workflow** di `.github/workflows/`:

| Workflow                | Trigger                      | Fungsi                                 |
| ----------------------- | ---------------------------- | -------------------------------------- |
| `ci.yml`                | Push & PR ke `main`          | Lint, doctor, docs, tests              |
| `deploy-cloudflare.yml` | Manual (`workflow_dispatch`) | Deploy ke `app.sertifikasitrainer.com` |

Repo GitHub: [trisnalesmana/aimentor](https://github.com/trisnalesmana/aimentor)\
Tab Actions: [github.com/trisnalesmana/aimentor/actions](https://github.com/trisnalesmana/aimentor/actions)

## Status saat ini

Ringkasan kondisi pipeline setelah workflow di-push ke `main`:

| Komponen                       | Status                     | Keterangan                               |
| ------------------------------ | -------------------------- | ---------------------------------------- |
| Workflow **CI**                | ✅ Aktif                    | Jalan otomatis tiap push/PR ke `main`    |
| Workflow **Deploy Cloudflare** | ✅ Ada, belum pernah di-run | Hanya manual lewat Actions               |
| CI run terakhir                | ✅ Hijau                    | Lint, doctor, docs, web tests lulus      |
| API tests di CI                | ✅ Blocking                 | `vitest run` di `apps/api` — 352 tests   |
| Environment `production`       | ✅ Dibuat                   | Masih perlu isi secrets sebelum deploy   |
| Repository secrets             | ❌ Belum diisi              | Deploy akan gagal tanpa secrets di bawah |

<Note>
  **CI sudah jalan.** **CD belum siap dipakai** sampai environment `production` dan secrets di-setup di GitHub.
</Note>

### Job CI — mana yang blocking?

| Job              | Blocking? | Catatan                               |
| ---------------- | --------- | ------------------------------------- |
| Lint (`lint:ci`) | Ya        | Hanya `scripts/`, `thub`, `.github/`  |
| thub doctor      | Ya        | Cek struktur repo, bukan secret nyata |
| Mintlify docs    | Ya        | `validate` + `broken-links`           |
| Web tests        | Ya        | Vitest di `apps/web`                  |
| API tests        | Ya        | `bun run test` (vitest) di `apps/api` |

## Cara cek sendiri

### Lewat browser

1. Buka [Actions](https://github.com/trisnalesmana/aimentor/actions)
2. Klik workflow **CI** — run hijau = pipeline utama lulus
3. Klik workflow **Deploy Cloudflare** — kalau kosong, belum pernah dijalankan
4. **Settings → Environments** — pastikan ada `production`
5. **Settings → Secrets and variables → Actions** — cek secret sudah terisi

### Lewat GitHub CLI

```bash theme={null}
# Workflow terdaftar
gh workflow list --repo trisnalesmana/aimentor

# Run CI terbaru
gh run list --repo trisnalesmana/aimentor --limit 5

# Detail satu run (ganti ID dari list di atas)
gh run view <RUN_ID> --repo trisnalesmana/aimentor

# Log job yang gagal
gh run view <RUN_ID> --repo trisnalesmana/aimentor --log-failed

# Cek secrets (nama saja, nilai tidak ditampilkan)
gh secret list --repo trisnalesmana/aimentor

# Cek environment
gh api repos/trisnalesmana/aimentor/environments
```

## Checklist setup CD

Centang ini sebelum menjalankan **Deploy Cloudflare** pertama kali:

<Steps>
  <Step title="Buat environment production">
    GitHub → **Settings → Environments → New environment** → nama: `production`\
    Opsional: tambah required reviewers supaya deploy butuh approval.
  </Step>

  <Step title="Isi DOTENV_SHARED">
    Salin **seluruh isi** file `.env` shared lokal (bukan path file).\
    Format: satu `KEY=value` per baris, multiline secret di GitHub.
  </Step>

  <Step title="Isi DATABASE_URL">
    URL Neon production untuk migrate saat deploy, misalnya:
    `postgresql://...@ep-xxx.neon.tech/neondb?sslmode=require`
  </Step>

  <Step title="Isi token Cloudflare">
    * `CLOUDFLARE_API_TOKEN` — token dengan akses Workers (Wrangler)
    * `CLOUDFLARE_ACCOUNT_ID` — dari dashboard Cloudflare
  </Step>

  <Step title="(Opsional) WORKER_SECRETS_B64">
    Kalau opsi *Push worker secrets* aktif saat deploy:

    ```bash theme={null}
    ./thub secrets generate
    base64 -w0 .cloudflare/worker-secrets.json
    ```

    Salin output ke secret `WORKER_SECRETS_B64`.
  </Step>

  <Step title="Jalankan deploy manual">
    Actions → **Deploy Cloudflare** → **Run workflow** → pilih branch `main`.
  </Step>
</Steps>

## CI (`ci.yml`)

Setiap push atau PR ke `main` menjalankan job paralel:

<AccordionGroup>
  <Accordion title="lint" icon="check">
    `bun run lint:ci` — Biome pada `scripts/`, `thub`, `.github/` (blocking).\
    `bun run lint` (seluruh monorepo) belum di-gate di CI karena masih ada lint debt legacy.
  </Accordion>

  <Accordion title="doctor" icon="stethoscope">
    `./thub doctor --ci` — cek struktur env, script, dan dependency (blocking).
  </Accordion>

  <Accordion title="docs" icon="book-open">
    `mint validate` + `mint broken-links` di `apps/docs` (blocking).
  </Accordion>

  <Accordion title="test-api" icon="flask">
    `bun run test` (vitest) di `apps/api` dengan `TZ=Asia/Jakarta` (blocking).
  </Accordion>

  <Accordion title="test-web" icon="flask">
    `bun run test` di `apps/web` (blocking).
  </Accordion>
</AccordionGroup>

### Simulasi lokal

```bash theme={null}
bun run lint:ci
./thub doctor --ci
cd apps/docs && npx -y mint@latest validate && npx -y mint@latest broken-links
cd apps/web && bun run test
cd apps/api && bun run test
```

## CD (`deploy-cloudflare.yml`)

Deploy **hanya manual** — tidak auto-deploy saat merge ke `main`.

<Steps>
  <Step title="Buat environment `production`">
    Di GitHub repo → **Settings → Environments** → tambah `production`.\
    Opsional: aktifkan required reviewers untuk gate deploy.
  </Step>

  <Step title="Isi repository secrets">
    | Secret                  | Wajib      | Keterangan                                    |
    | ----------------------- | ---------- | --------------------------------------------- |
    | `DOTENV_SHARED`         | Ya         | Isi penuh file `.env` shared (multiline)      |
    | `DATABASE_URL`          | Ya         | URL Neon untuk migrate production             |
    | `CLOUDFLARE_API_TOKEN`  | Ya         | Token Wrangler dengan akses Workers           |
    | `CLOUDFLARE_ACCOUNT_ID` | Ya         | Account ID Cloudflare                         |
    | `WORKER_SECRETS_B64`    | Opsional\* | Base64 dari `.cloudflare/worker-secrets.json` |

    \* Diperlukan jika opsi **Push worker secrets** diaktifkan saat deploy.
  </Step>

  <Step title="Generate WORKER_SECRETS_B64">
    ```bash theme={null}
    ./thub secrets generate
    base64 -w0 .cloudflare/worker-secrets.json
    ```

    Salin output ke secret `WORKER_SECRETS_B64`.
  </Step>

  <Step title="Jalankan workflow">
    GitHub → **Actions** → **Deploy Cloudflare** → **Run workflow**.

    Input opsional:

    | Input                   | Default | Arti                                    |
    | ----------------------- | ------- | --------------------------------------- |
    | Skip database migration | `false` | Lewati `psql` migrate                   |
    | Skip web build          | `false` | Deploy tanpa rebuild frontend           |
    | Push worker secrets     | `true`  | Sync secrets ke Wrangler sebelum deploy |
    | Run smoke test          | `true`  | `./thub smoke prod` setelah deploy      |
  </Step>
</Steps>

### Apa yang dijalankan di CI deploy

1. `scripts/ci-materialize-env.sh` — tulis `.env` dari `DOTENV_SHARED`, optional `DATABASE_URL` → `.env.production.local`, lalu `./thub env merge --prod`
2. `scripts/ci-push-worker-secrets.sh` — decode `WORKER_SECRETS_B64` → push via `sync-worker-secrets.sh`
3. `scripts/deploy-prod.sh` — migrate, build, deploy Workers
4. `./thub smoke prod` — health check production

## Perbandingan dengan deploy lokal

|            | Lokal (`./thub deploy`)      | GitHub Actions                   |
| ---------- | ---------------------------- | -------------------------------- |
| Env        | `.env` + override lokal      | Secret `DOTENV_SHARED`           |
| Konfirmasi | Ketik `yes`                  | Workflow dispatch                |
| Secrets    | `./thub secrets push`        | `WORKER_SECRETS_B64` + script CI |
| Target     | `app.sertifikasitrainer.com` | Sama                             |

Deploy lokal tetap valid untuk hotfix cepat; CI/CD untuk audit trail dan env terpusat di GitHub.

## Troubleshooting CI

<AccordionGroup>
  <Accordion title="Semua job gagal di bun install --frozen-lockfile" icon="circle-alert">
    Pesan: `lockfile had changes, but lockfile is frozen`.

    Penyebab: `package.json` berubah (misalnya workspace `apps/docs`) tapi `bun.lock` belum di-commit.

    Perbaikan lokal:

    ```bash theme={null}
    bun install
    git add bun.lock
    git commit -m "fix(ci): sync bun.lock"
    git push
    ```
  </Accordion>

  <Accordion title="doctor gagal di CI tapi lokal OK" icon="circle-alert">
    Pastikan `bun install --frozen-lockfile` sukses dan tidak ada file `.env` yang di-commit. Doctor CI tidak butuh secret nyata — hanya struktur repo.
  </Accordion>

  <Accordion title="API tests gagal di CI" icon="circle-alert">
    Saat ini **tidak menggagalkan** workflow (`continue-on-error`). Banyak test API butuh env lengkap atau masih assertion lama. Job ini informatif dulu — perbaikan test API bisa dilakukan bertahap.
  </Accordion>

  <Accordion title="deploy gagal: Missing DOTENV_SHARED" icon="circle-alert">
    Secret harus berisi **seluruh** isi `.env` shared, bukan path file. Format `KEY=value` per baris.
  </Accordion>

  <Accordion title="migrate gagal" icon="circle-alert">
    Cek `DATABASE_URL` di environment `production`. URL harus reachable dari runner GitHub (Neon dengan SSL).
  </Accordion>

  <Accordion title="smoke prod gagal setelah deploy" icon="circle-alert">
    Tunggu propagasi Cloudflare (\~1 menit), lalu re-run hanya smoke: `./thub smoke prod` lokal dengan env production, atau re-run workflow dengan skip migrate/build.
  </Accordion>
</AccordionGroup>
