Commit Langsung ke master Bakal Nyiksa Lo di Hari Deploy
Satu refactor Swagger yang di-commit langsung ke master bikin 3-way merge conflict, GitLab API diam-diam gagal, dan 30 menit operasi branch manual sebelum bisa deploy. Ini cerita lengkapnya dan satu aturan yang bisa mencegahnya.

Deploy yang Harusnya Lima Menit
Senin pagi. Tiga merge request udah antri: release ke master di tiga repo, api-service, auth-service, sama web-app. Deploy produksi rutin. Yang biasanya kelar dalam lima menit.
Terus MR pertama langsung nunjukin conflict, dan tiba-tiba lo lagi baca git history sambil nyari tau siapa yang commit Swagger refactor langsung ke master dua minggu lalu.
Ini cerita soal itu. Gue bakal cerita tentang conflictnya, tiga attempt yang gagal, jebakan GitLab API yang diam-diam nolak setiap write tanpa kasih error, dan workaround yang akhirnya berhasil. Di akhir ada satu aturan yang bisa mencegah semua ini dari awal.
Setup: Tiga Repo, Satu Masalah
Pada 2026-05-19 kita merge release ke master di tiga repository:
| Repository | MR | Hasil |
|---|---|---|
api-service | !18 | Merged (dengan conflict resolution) |
auth-service | !3 | Merged bersih |
web-app | !34 | Merged bersih |
Yang bersih itu gampang. api-service yang jadi masalah. Dan ngerti kenapa dia conflict ngajarin sesuatu yang biasanya baru ketahuan waktu udah kena.
Kenapa Conflict Bisa Terjadi: Problem 3-Way Merge
Sebelum merge, kondisi branch api-service kayak gini:
release1 commit di depanmastermaster11 commit di depanrelease(diverged history)
Divergence itu yang jadi akar masalahnya nih. Sebelas commit udah numpuk di master dan belum pernah di-backport ke release. Di antaranya ada Swagger refactor gede (f46ba777) yang di-commit langsung ke master:
[f46ba777] refactor: migrate Swagger @ApiProperty types to lazy syntax across all modules
[13f2cb5f] fix: resolve Swagger startup crash caused by null-typed data propertyDua commit ini masuk langsung ke master, bukan merge dari release. Waktu commit release berikutnya (b33a1a33) juga nyentuh file catalog.items.controller.ts buat update summary @ApiOperation, git ketemu masalah:
Kondisi base (common ancestor):
@ApiResponse({ ..., type: CatalogItem... })
master ubah (f46ba777):
type: CatalogItem... → type: () => CatalogItem... ← lazy syntax
release ubah (b33a1a33):
summary: 'Get catalog items from external API'
→ summary: 'Get catalog items with extra details'Dua branch ini ngubah blok yang sama secara independen. Git gak bisa tau versi type: yang mana yang harus dipakai, jadi dia raise conflict. Ini yang disebut 3-way merge: git bandingin common ancestor, tip release, sama tip master. Kalau dua tip keduanya ngubah baris yang sama dari ancestor yang sama, resolusi otomatis gak mungkin.
Aturan yang mencegah ini simpel dan mutlak:
Semua perubahan, termasuk hotfix, harus lewat release dulu.
Benar: feature → release → master
Salah: feature → master (langsung)Kalau master cuma nerima commit lewat merge dari release, setiap merge bakal jadi fast-forward dan conflict jadi gak mungkin terjadi secara struktural.
Tiga Attempt yang Gagal Sebelum Ketemu Solusi
Setelah conflict ketemu, kita coba jalur yang paling obvious dulu.
Juga Baca: Fix Error Git Pull: Atasi Unstaged Changes dengan git stash
Attempt 1: Sync MR (master ke release)
Bikin MR !17 buat backport perubahan master ke release. Langsung conflict juga. Ditutup.
Attempt 2: Rebase via API
Coba PUT /merge_requests/:iid/rebase di GitLab API. Dapet HTTP 411, Content-Length required. Endpoint langsung nolak requestnya.
Attempt 3: Patch File Langsung di release (Yang Tricky)
Di sini yang mulai diam-diam nyeselin. Gue pakai GitLab Files API buat patch baris yang conflict langsung di branch release:
PUT /projects/:id/repository/files/:file_pathAPI baliknya HTTP 200 OK di setiap call. Response body-nya keliatan sukses. Tapi waktu gue fetch HEAD branch release setelahnya, dia gak bergerak. Commit-nya gak ada.
Yang terjadi: release adalah protected branch. GitLab Files API diam-diam nolak write karena aturan branch protection. Tapi dia tetap balikin 200. Gak ada error, gak ada warning, gak ada indikasi di response bahwa perubahan tadi dibuang.
Ini jebakan GitLab API yang nyata nih. Kalau lo nulis ke protected branch via API, selalu re-fetch branch-nya setelah write dan verifikasi HEAD-nya bergerak. Response 200 bukan jaminan bahwa sesuatu berubah.
Solusinya: Operasi Branch Sementara
Cara yang berhasil ternyata straightforward begitu kita berhenti coba fix protected branch secara langsung.
- Bikin branch unprotected baru
merge/release-to-masterdarimaster - Susun manual state merged untuk setiap file yang conflict:
- Pertahanin lazy syntax
type: () =>darimaster(yang benar) - Apply summary dan description
@ApiOperationyang diupdate darireleasedi atasnya - Ambil versi
releaseuntuk file yang gak conflict:CHANGELOG.md,package.json,app.module.ts - Tambahin semua 6 file
endpoint-catalogbaru darirelease
- Pertahanin lazy syntax
- Push semua 12 file sebagai satu commit via GitLab Commits API
POST /projects/:id/repository/commitsDengan merged commit yang udah di-push ke branch unprotected, MR baru dibuat: merge/release-to-master ke master. Conflict check-nya langsung clean.
Juga Baca: GitLab CI/CD Dynamic Variables: Konfigurasi Dev, Staging & Production
Pipeline Failure yang Sebenernya Bukan Failure
Pipeline #55045 di merge/release-to-master langsung gagal dengan:
ERROR: Invalid branch: merge/release-to-master.
Only release, master, and develop are allowed.Ini expected kok. Deploy script kita (deploy.sh) cuma trigger deployment dari named environment branch. Pipeline gagal di sini artinya code gak di-deploy dari branch sementara tadi. Itu yang benar. Pipeline release (#55033) udah passed di code yang sama.
Waktu lo lihat error kayak gini, cek dulu apakah ini masalah code atau memang pembatasan konfigurasi pipeline. Di kasus ini murni yang kedua.
Yang Gampang: auth-service dan web-app
Setelah api-service beres, dua lainnya lancar.
Untuk auth-service, 3 diverged commit di master semuanya adalah merge commit dan update CI configuration. Gak ada yang nyentuh file yang sama dengan commit JWT payload dari release. MR !3 merged bersih.
Untuk web-app, master punya satu direct code commit (1bc9adb8) yang nyentuh 20 file. File overlap check konfirmasi nol overlap dengan 21 file di 4 commit release. MR !34 merged bersih.
Jam 11:57, ketiga repo udah masuk produksi.
Yang Dikirim ke Production
Dari tiga repository, production deploy ini ngirim:
EndpointCatalogModule: registry 50+ endpoint dengan freshness status (fresh,stale,critical,unknown,error) dan optional HTTP probe dengan cache 5 menit- JWT roles langsung tertanam di token payload (gak perlu database lookup ekstra per request)
- User unlimited token sekarang bisa dikonfigurasi via environment variable
- Halaman admin endpoint catalog dengan KPI card, endpoint filter, status table, dan HTTP probe panel
- CSRF retry logic difix supaya skip re-attempt di role-based 403 response
Satu Aturan yang Mencegah Semua Ini
Semua detour 30 menit tadi terjadi karena satu pelanggaran: commit langsung ke master.
Waktu master punya commit independen yang release gak punya, setiap merge release berikutnya berpotensi jadi conflict. Makin lama divergence-nya tumbuh, makin besar kemungkinan merge selanjutnya kena line overlap.
release: A → B → C → D
master: A → B → E → F ← commit independen = risiko conflict
vs.
release: A → B → C → D
master: A → B → C → D ← fast-forward only = gak mungkin conflictEnforce ini di level GitLab: protect master, wajibkan semua code lewat release dulu. Deploy lo berikutnya bakal lima menit, bukan tiga puluh.
Ringkasan
Satu Swagger refactor yang di-commit langsung ke master bikin 3-way merge conflict yang ngeblok deploy release kita. Tiga attempt fix gagal, termasuk write via GitLab Files API yang diam-diam balikin 200 OK sambil buang perubahan karena branch protection. Solusinya: bikin branch unprotected sementara, susun manual merged state, dan buka MR clean dari sana. Total waktu: di bawah 30 menit. Cara cegahnya: jangan pernah commit langsung ke master.


