<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>DevOps on kenji.blog</title><link>http://kenji.blog/id/categories/devops/</link><description>Recent content in DevOps on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>id</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 09:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/id/categories/devops/index.xml" rel="self" type="application/rss+xml"/><item><title>【Perintah Git】Perbedaan antara rebase dan merge, serta penggunaan yang tepat dalam praktik</title><link>http://kenji.blog/id/p/git-rebase-vs-merge-practical-guide/</link><pubDate>Sun, 13 Sep 2026 09:00:00 +0900</pubDate><guid>http://kenji.blog/id/p/git-rebase-vs-merge-practical-guide/</guid><description>&lt;img src="http://kenji.blog/p/git-rebase-vs-merge-practical-guide/img/eyecatch.jpg" alt="Featured image of post 【Perintah Git】Perbedaan antara rebase dan merge, serta penggunaan yang tepat dalam praktik" />&lt;h1 id="1-pendahuluan-mengapa-merge-atau-rebase-menjadi-perdebatan-abadi">1. Pendahuluan: Mengapa &amp;ldquo;merge atau rebase&amp;rdquo; menjadi perdebatan abadi
&lt;/h1>&lt;p>Git adalah sistem pengontrol versi yang sangat penting dalam pengembangan perangkat lunak modern. Saat beberapa pengembang mengubah basis kode secara bersamaan, model cabang (branching) Git yang kuat akan sangat berguna. Namun, dalam pengembangan tim, perdebatan tentang &amp;ldquo;apakah harus menggunakan merge atau rebase&amp;rdquo; merupakan salah satu topik yang selalu membingungkan pengembang, mulai dari pemula hingga ahli.&lt;/p>
&lt;p>Artikel ini akan mengupas perbedaan mekanisme antara &lt;code>git merge&lt;/code> dan &lt;code>git rebase&lt;/code> secara mendalam, mulai dari struktur internal Git yaitu DAG (Directed Acyclic Graph) dan sifat matematis dari hash komit. Selain itu, artikel ini juga akan menjelaskan secara menyeluruh bagaimana cara menggunakan keduanya secara tepat dalam praktik, disertai dengan alur kerja (workflow) yang konkret. Dengan memahami tidak hanya sekadar pengenalan perintah, tetapi juga perhitungan apa yang dilakukan Git di balik layar, Anda akan menghilangkan ketakutan terhadap konflik dan mampu membangun riwayat yang bersih serta mudah dilacak.&lt;/p>
&lt;hr>
&lt;h1 id="2-struktur-internal-git-hash-komit-dan-model-objek">2. Struktur Internal Git: Hash Komit dan Model Objek
&lt;/h1>&lt;p>Untuk memahami bagaimana Git mengintegrasikan riwayat, pertama-tama kita perlu mengetahui bagaimana Git menyimpan data. Git tidak sekadar menyimpan perbedaan (patch) dari perubahan file, melainkan menyimpan cuplikan (snapshot) dari seluruh sistem file pada suatu titik waktu.&lt;/p>
&lt;h2 id="21-sifat-kriptografi-dari-hash-komit">2.1 Sifat Kriptografi dari Hash Komit
&lt;/h2>&lt;p>Setiap komit Git diidentifikasi secara unik dengan 40 digit heksadesimal yang dihasilkan oleh fungsi hash SHA-1 (Secure Hash Algorithm 1) berdasarkan isinya. Objek komit terdiri dari elemen-elemen berikut:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Pointer ke objek Tree&lt;/strong>: Cuplikan struktur direktori dan file (Blob) pada saat itu.&lt;/li>
&lt;li>&lt;strong>Pointer ke komit induk (parent)&lt;/strong>: Nilai hash dari satu atau lebih komit induk (komit pertama tidak memiliki induk, sedangkan komit merge memiliki dua induk atau lebih).&lt;/li>
&lt;li>&lt;strong>Informasi pembuat (Author)&lt;/strong>: Orang yang menulis kode beserta tanggal dan waktunya.&lt;/li>
&lt;li>&lt;strong>Informasi committer (Committer)&lt;/strong>: Orang yang membuat/menerapkan komit beserta tanggal dan waktunya.&lt;/li>
&lt;li>&lt;strong>Pesan komit&lt;/strong>: Teks yang menjelaskan maksud perubahan.&lt;/li>
&lt;/ol>
&lt;p>Secara matematis, nilai hash $H(C)$ untuk objek komit $C$ didefinisikan sebagai berikut:&lt;/p>
$$
H(C) = \text{SHA-1}( \text{tree} \parallel \text{parent} \parallel \text{author} \parallel \text{committer} \parallel \text{message} )
$$&lt;p>Di sini, $\parallel$ merepresentasikan penggabungan data. Karena karakteristik fungsi hash, meskipun hanya mengubah satu karakter pada pesan komit, atau jika komit induknya berbeda, nilai hash yang dihasilkan akan benar-benar berbeda. Artinya, &lt;strong>komit bersifat tidak dapat diubah (Immutable)&lt;/strong>. Alasan mengapa &lt;code>rebase&lt;/code> yang akan dibahas nanti sering disebut &amp;ldquo;menulis ulang riwayat&amp;rdquo; sebenarnya adalah karena Git &amp;ldquo;membuat komit baru yang isinya mirip tetapi memiliki nilai hash yang berbeda&amp;rdquo;.&lt;/p>
&lt;p>Ukuran ruang hash adalah $2^{160}$, dan probabilitas $P$ terjadinya tabrakan (dua komit berbeda memiliki nilai hash yang sama) dapat diaproksimasi menggunakan teori Paradoks Ulang Tahun (Birthday Paradox) sebagai berikut ($n$ adalah jumlah komit):&lt;/p>
$$
P(\text{collision}) \approx 1 - \exp\left(-\frac{n^2}{2 \times 2^{160}}\right)
$$&lt;p>Probabilitas ini sangat rendah, sehingga secara praktis hampir tidak mungkin terjadi tabrakan hash komit pada Git.&lt;/p>
&lt;hr>
&lt;h1 id="3-teori-graf-dan-dag-model-matematis-riwayat-git">3. Teori Graf dan DAG: Model Matematis Riwayat Git
&lt;/h1>&lt;p>Riwayat komit Git dimodelkan sebagai &amp;ldquo;Directed Acyclic Graph (DAG)&amp;rdquo; dalam teori graf.&lt;/p>
&lt;h2 id="31-apa-itu-dag-directed-acyclic-graph">3.1 Apa itu DAG (Directed Acyclic Graph)?
&lt;/h2>&lt;p>Dalam graf $G = (V, E)$, $V$ adalah himpunan komit (simpul/vertex), dan $E$ adalah himpunan edge berarah yang menunjukkan hubungan induk-anak antar komit. Pada Git, arah edge menuju dari &amp;ldquo;komit anak ke komit induk&amp;rdquo;. Hal ini karena komit baru menyimpan pointer ke komit sebelumnya.&lt;/p>
&lt;pre class="mermaid">
graph BT
A[&amp;#34;Komit A (Awal)&amp;#34;]
B[&amp;#34;Komit B&amp;#34;]
C[&amp;#34;Komit C (Main)&amp;#34;]
D[&amp;#34;Komit D (Fitur)&amp;#34;]
E[&amp;#34;Komit E (Merge)&amp;#34;]
B --&amp;gt; A
C --&amp;gt; B
D --&amp;gt; B
E --&amp;gt; C
E --&amp;gt; D
&lt;/pre>
&lt;p>Fitur utama DAG adalah &amp;ldquo;tidak adanya siklus (putaran)&amp;rdquo;. Dengan ini, algoritma yang menelusuri riwayat komit tidak akan terjebak dalam loop tak terbatas, dan pasti akan mencapai titik akhir (komit awal).&lt;/p>
&lt;h2 id="32-topological-sort-dan-urutan-riwayat">3.2 Topological Sort dan Urutan Riwayat
&lt;/h2>&lt;p>Saat menampilkan riwayat menggunakan perintah &lt;code>git log&lt;/code>, DAG diurutkan sebagai daftar 1 dimensi menggunakan algoritma Topological Sort (Pengurutan Topologi). Untuk setiap edge berarah $u \to v$ dalam DAG ($u$ adalah anak dari $v$), daftar disusun sedemikian rupa sehingga $u$ selalu berada sebelum $v$.&lt;/p>
&lt;hr>
&lt;h1 id="4-mekanisme-dan-jenis-git-merge">4. Mekanisme dan Jenis git merge
&lt;/h1>&lt;p>Perintah paling dasar untuk mengintegrasikan perubahan pada cabang adalah &lt;code>git merge&lt;/code>. Namun, bergantung pada status saat ini, Git secara otomatis akan memilih strategi merge yang berbeda.&lt;/p>
&lt;h2 id="41-merge-fast-forward---ff">4.1 Merge Fast-Forward (&amp;ndash;ff)
&lt;/h2>&lt;p>Jika cabang tujuan integrasi (misalnya: &lt;code>main&lt;/code>) adalah leluhur langsung dari cabang sumber integrasi (misalnya: &lt;code>feature&lt;/code>), Git akan menjalankan merge &amp;ldquo;Fast-Forward&amp;rdquo;. Ini adalah operasi yang tidak membuat komit baru, melainkan hanya memajukan pointer cabang ke depan.&lt;/p>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;A&amp;#34;
commit id: &amp;#34;B&amp;#34;
branch feature
checkout feature
commit id: &amp;#34;C&amp;#34;
commit id: &amp;#34;D&amp;#34;
checkout main
merge feature
&lt;/pre>
&lt;p>Merge Fast-Forward menjaga riwayat tetap dalam satu garis lurus, namun memiliki kelemahan yaitu hilangnya konteks &amp;ldquo;kelompok komit mana yang digabungkan sebagai satu pengembangan fitur (feature)&amp;rdquo;.&lt;/p>
&lt;h2 id="42-merge-non-fast-forward---no-ff">4.2 Merge Non-Fast-Forward (&amp;ndash;no-ff)
&lt;/h2>&lt;p>Jika Anda secara eksplisit menentukan &lt;code>git merge --no-ff&lt;/code>, Git akan selalu membuat &amp;ldquo;komit merge&amp;rdquo; baru meskipun situasi tersebut memungkinkan Fast-Forward. Komit merge adalah komit khusus yang memiliki dua induk.&lt;/p>
&lt;pre class="mermaid">
gitGraph
commit id: &amp;#34;A&amp;#34;
commit id: &amp;#34;B&amp;#34;
branch feature
checkout feature
commit id: &amp;#34;C&amp;#34;
commit id: &amp;#34;D&amp;#34;
checkout main
commit id: &amp;#34;Main Work 1&amp;#34;
merge feature type: NORMAL
&lt;/pre>
&lt;p>Keuntungan dari metode ini adalah bahwa keberadaan dan riwayat cabang fitur tetap terekam dengan jelas pada DAG. Saat terjadi masalah, Anda dapat membatalkan (revert) seluruh fitur sekaligus dengan aman dengan menjalankan &lt;code>git revert -m 1 &amp;lt;hash komit merge&amp;gt;&lt;/code>.&lt;/p>
&lt;h2 id="43-algoritma-3-way-merge">4.3 Algoritma 3-Way Merge
&lt;/h2>&lt;p>Ketika cabang tujuan dan cabang sumber masing-masing memiliki komitnya sendiri, Git akan menjalankan 3-Way Merge (merge 3 arah). Pada saat ini, Git menelusuri DAG dan menemukan &amp;ldquo;leluhur umum (Lowest Common Ancestor, LCA)&amp;rdquo; dari kedua cabang tersebut.&lt;/p>
&lt;p>Kompleksitas perhitungan algoritma untuk menemukan LCA, $T_{\text{LCA}}$, dapat dieksekusi dalam waktu linear terhadap jumlah simpul $|V|$ dan jumlah edge $|E|$:&lt;/p>
$$
T_{\text{LCA}} = \mathcal{O}(|V| + |E|)
$$&lt;p>Git membandingkan tiga hal: &amp;ldquo;status LCA&amp;rdquo;, &amp;ldquo;status cabang saat ini&amp;rdquo;, dan &amp;ldquo;status cabang lawan&amp;rdquo;. Jika tidak ada konflik perubahan, Git secara otomatis akan menghasilkan komit merge.&lt;/p>
&lt;hr>
&lt;h1 id="5-mekanisme-git-rebase-dan-rekonstruksi-riwayat">5. Mekanisme git rebase dan Rekonstruksi Riwayat
&lt;/h1>&lt;p>Jika &lt;code>git merge&lt;/code> &amp;ldquo;mengintegrasikan&amp;rdquo; riwayat, maka &lt;code>git rebase&lt;/code> &amp;ldquo;merekonstruksi (memindahkan)&amp;rdquo; riwayat.&lt;/p>
&lt;h2 id="51-pergerakan-di-balik-rebase">5.1 Pergerakan di Balik Rebase
&lt;/h2>&lt;p>Operasi internal saat melakukan rebase pada cabang &lt;code>feature&lt;/code> ke cabang &lt;code>main&lt;/code> (&lt;code>git rebase main&lt;/code>) adalah sebagai berikut:&lt;/p>
&lt;ol>
&lt;li>Menemukan leluhur umum (LCA) antara cabang &lt;code>feature&lt;/code> dan cabang &lt;code>main&lt;/code>.&lt;/li>
&lt;li>Menyimpan sementara perbedaan komit dari LCA hingga ujung cabang &lt;code>feature&lt;/code>.&lt;/li>
&lt;li>Memindahkan pointer cabang &lt;code>feature&lt;/code> ke ujung cabang &lt;code>main&lt;/code>.&lt;/li>
&lt;li>Menerapkan (Cherry-Pick) perbedaan yang disimpan tadi satu per satu secara berurutan ke atas base baru (ujung &lt;code>main&lt;/code>), dan membuat komit baru.&lt;/li>
&lt;/ol>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Komit A&amp;#34;] --&amp;gt; B[&amp;#34;Komit B&amp;#34;]
B --&amp;gt; C[&amp;#34;Komit C (Main)&amp;#34;]
B --&amp;gt; D[&amp;#34;Komit D (Fitur Lama)&amp;#34;]
D -.-&amp;gt; E[&amp;#34;Komit D&amp;#39; (Fitur Baru)&amp;#34;]
C --&amp;gt; E
style D stroke-dasharray: 5 5, fill: #f9f9f9, color: #999
&lt;/pre>
&lt;p>Hal penting di sini adalah komit yang dihasilkan oleh rebase $D'$ memiliki &lt;strong>induk komit yang berbeda dari komit aslinya $D$, sehingga memiliki nilai hash yang benar-benar berbeda&lt;/strong> (merujuk pada definisi fungsi hash $H(C)$ yang disebutkan sebelumnya).&lt;/p>
&lt;h2 id="52-rebase-interaktif-interactive-rebase">5.2 Rebase Interaktif (Interactive Rebase)
&lt;/h2>&lt;p>Dengan menggunakan &lt;code>git rebase -i&lt;/code> (atau &lt;code>--interactive&lt;/code>), Anda dapat memanipulasi riwayat komit sesuka hati. Ini adalah alat paling ampuh untuk merapikan riwayat lokal.&lt;/p>
&lt;ul>
&lt;li>&lt;code>pick&lt;/code> : Menerima komit apa adanya&lt;/li>
&lt;li>&lt;code>reword&lt;/code> : Hanya mengubah pesan komit&lt;/li>
&lt;li>&lt;code>edit&lt;/code> : Menjeda untuk memodifikasi isi komit&lt;/li>
&lt;li>&lt;code>squash&lt;/code> : Menggabungkan komit ini dengan komit sebelumnya, dan juga menggabungkan pesannya&lt;/li>
&lt;li>&lt;code>fixup&lt;/code> : Sama seperti &lt;code>squash&lt;/code>, tetapi membuang pesan dari komit ini&lt;/li>
&lt;li>&lt;code>drop&lt;/code> : Menghapus komit sepenuhnya&lt;/li>
&lt;/ul>
&lt;p>Secara matematis, jika sebuah cabang memiliki $N$ komit, variasi (permutasi) riwayat linear $P$ yang dapat dihasilkan dengan mengubah urutan rebase adalah sebagai berikut:&lt;/p>
$$
P = N!
$$&lt;p>Git memberikan $N!$ macam kebebasan kepada pengembang, memungkinkan mereka untuk menjaga riwayat dalam kondisi yang logis dan indah.&lt;/p>
&lt;hr>
&lt;h1 id="6-aturan-emas-rebase-the-golden-rule-of-rebase">6. Aturan Emas Rebase (The Golden Rule of Rebase)
&lt;/h1>&lt;p>&lt;code>rebase&lt;/code> sangatlah kuat, namun terdapat satu aturan mutlak.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>&amp;ldquo;Jangan pernah melakukan rebase pada riwayat publik yang telah dibagikan.&amp;rdquo;&lt;/strong>
&lt;em>(Never rebase public history)&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;h2 id="61-mengapa-kita-tidak-boleh-melakukan-rebase-pada-riwayat-publik">6.1 Mengapa Kita Tidak Boleh Melakukan Rebase pada Riwayat Publik?
&lt;/h2>&lt;p>Git bersifat terdistribusi. Komit yang Anda dorong (push) ke &lt;code>origin/main&lt;/code> juga di-kloning (disalin) ke repositori lokal milik pengembang lain. Jika Anda merebase komit yang sudah di-push untuk menulis ulang riwayat, dan memaksakan penimpaan dengan &lt;code>git push --force&lt;/code>, apa yang akan terjadi?&lt;/p>
&lt;p>DAG lokal milik pengembang lain dan DAG jarak jauh (remote) akan bercabang secara mendasar. Ketika pengembang lain menjalankan &lt;code>git pull&lt;/code>, Git akan mencoba memaksa penggabungan sekelompok komit yang memiliki sejarah berbeda. Hal ini akan menyebabkan banyak konflik dan komit duplikat (komit dengan perubahan yang sama tetapi hash yang berbeda), sehingga membuat repositori menjadi berantakan.&lt;/p>
&lt;p>Aturan emasnya adalah rebase &lt;strong>hanya boleh dilakukan pada &amp;ldquo;cabang lokal yang belum dibagikan kepada siapa pun&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="7-penyelesaian-konflik-dan-git-rebase---continue">7. Penyelesaian Konflik dan git rebase &amp;ndash;continue
&lt;/h1>&lt;p>Ketika beberapa orang mengubah bagian yang sama dari file yang sama, konflik akan terjadi. Proses penyelesaian konflik antara &lt;code>merge&lt;/code> dan &lt;code>rebase&lt;/code> berbeda.&lt;/p>
&lt;h2 id="71-penyelesaian-konflik-pada-merge">7.1 Penyelesaian Konflik pada Merge
&lt;/h2>&lt;p>Pada &lt;code>git merge&lt;/code>, penyelesaian konflik hanya terjadi &lt;strong>satu kali&lt;/strong>. Sesaat sebelum membuat komit merge akhir, Anda memperbaiki semua bagian yang konflik sekaligus.&lt;/p>
&lt;h2 id="72-penyelesaian-konflik-pada-rebase">7.2 Penyelesaian Konflik pada Rebase
&lt;/h2>&lt;p>Pada &lt;code>git rebase&lt;/code>, karena sifatnya yang menerapkan ulang komit satu per satu, ada kemungkinan &lt;strong>konflik terjadi pada setiap komit&lt;/strong>.&lt;/p>
&lt;p>Jika konflik terjadi selama rebase, Git akan menjeda prosesnya. Alur penyelesaiannya adalah sebagai berikut:&lt;/p>
&lt;ol>
&lt;li>Buka editor atau IDE (seperti VS Code) dan perbaiki penanda konflik (seperti &lt;code>&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/code>, &lt;code>======&lt;/code>, &lt;code>&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code>) secara manual.&lt;/li>
&lt;li>Tambahkan file yang telah diperbaiki ke dalam indeks:
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git add &amp;lt;file yang diperbaiki&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>Tanpa membuat komit baru, lanjutkan proses rebase:
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git rebase --continue
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;/ol>
&lt;p>Jika Anda ingin membatalkan rebase itu sendiri dan kembali ke keadaan semula, jalankan perintah berikut:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git rebase --abort
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>（&lt;em>Jika penyelesaian konflik tidak diperlukan dan Anda ingin melewati komit tersebut, gunakan &lt;code>git rebase --skip&lt;/code>&lt;/em>)&lt;/p>
&lt;hr>
&lt;h1 id="8-penggunaan-yang-tepat-dalam-praktik-praktik-workflow">8. Penggunaan yang Tepat dalam Praktik (Praktik Workflow)
&lt;/h1>&lt;p>Lalu, bagaimana sebaiknya kita menggunakan &lt;code>merge&lt;/code> dan &lt;code>rebase&lt;/code> dalam praktik pengembangan yang sebenarnya? Di sini kita akan memperkenalkan pendekatan yang paling standar dan aman.&lt;/p>
&lt;h2 id="81-skenario-1-merapikan-riwayat-pekerjaan-lokal-menggunakan-rebase">8.1 【Skenario 1】 Merapikan Riwayat Pekerjaan Lokal (Menggunakan Rebase)
&lt;/h2>&lt;p>Misalkan saat mengembangkan di cabang fitur, banyak komit kecil menumpuk (seperti &amp;ldquo;Perbaikan typo&amp;rdquo; atau &amp;ldquo;Simpan sementara&amp;rdquo;). Sebelum membuat Pull Request (PR), gunakan rebase interaktif untuk merapikannya ke dalam unit-unit yang bermakna.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Jalankan saat berada di cabang feature&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rebase -i HEAD~5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># (Editor akan terbuka, gunakan squash atau fixup untuk membersihkan riwayat)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dengan melakukan ini, Anda dapat membuat riwayat komit yang indah dan maksudnya mudah dipahami oleh pengulas (reviewer).&lt;/p>
&lt;h2 id="82-skenario-2-mengikuti-pembaruan-cabang-main-terbaru-menggunakan-rebase">8.2 【Skenario 2】 Mengikuti Pembaruan Cabang main Terbaru (Menggunakan Rebase)
&lt;/h2>&lt;p>Jika pengembangan memakan waktu lama dan perubahan dari orang lain terus menerus di-merge ke cabang &lt;code>main&lt;/code>, cabang &lt;code>feature&lt;/code> milik Anda akan menjadi usang. Dalam kasus ini, lakukan rebase cabang &lt;code>feature&lt;/code> Anda ke cabang &lt;code>main&lt;/code> terbaru agar tetap sinkron.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Dapatkan informasi terbaru dari main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git fetch origin
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Pindahkan cabang feature ke atas main terbaru&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git rebase origin/main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dengan cara ini, riwayat menjadi satu garis lurus dan dapat mencegah konflik pada merge selanjutnya. Selain itu, Anda juga mencegah pembuatan komit merge yang tidak perlu (seperti &amp;ldquo;Merge branch &amp;lsquo;main&amp;rsquo; into feature&amp;rdquo;).&lt;/p>
&lt;h2 id="83-skenario-3-mengintegrasikan-fitur-yang-telah-selesai-menggunakan-merge">8.3 【Skenario 3】 Mengintegrasikan Fitur yang Telah Selesai (Menggunakan Merge)
&lt;/h2>&lt;p>Pengembangan pada cabang &lt;code>feature&lt;/code> telah selesai, dan sekarang saatnya memasuki fase integrasi ke cabang &lt;code>main&lt;/code>. Di sini kita menggunakan &lt;strong>&lt;code>git merge --no-ff&lt;/code>&lt;/strong> (sama seperti memilih &amp;ldquo;Create a merge commit&amp;rdquo; di Pull Request pada GitHub, dll.).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">git checkout main
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git merge --no-ff feature -m &lt;span class="s2">&amp;#34;Merge feature: Implementasi fitur login pengguna&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">git push origin main
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dengan ini, sebuah titik sejarah (komit merge) yang menunjukkan &amp;ldquo;satu fitur digabungkan di sini&amp;rdquo; akan tertinggal pada DAG cabang &lt;code>main&lt;/code>. Saat Anda melihat riwayat di kemudian hari, akan lebih mudah untuk menelusuri kode berdasarkan unit fitur.&lt;/p>
&lt;hr>
&lt;h1 id="9-kesimpulan-ringkasan">9. Kesimpulan (Ringkasan)
&lt;/h1>&lt;p>Dalam pengoperasian Git, pendekatan ekstrem seperti &amp;ldquo;selesaikan semuanya dengan Merge&amp;rdquo; atau &amp;ldquo;buat semuanya lurus dengan Rebase&amp;rdquo; masing-masing memiliki kelebihan dan kekurangan.&lt;/p>
&lt;p>Praktik terbaik (best practice) dalam bekerja adalah pendekatan hibrida: &lt;strong>&amp;ldquo;Rapikan riwayat pribadi lokal dengan rebase, dan pertahankan konteks riwayat integrasi publik menggunakan merge &amp;ndash;no-ff.&amp;rdquo;&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Lokal (Ruang Kerja Pribadi)&lt;/strong>: Gunakan &lt;code>rebase&lt;/code> untuk membuang komit yang tidak perlu dan mengikuti mainline terbaru agar riwayat tetap lurus.&lt;/li>
&lt;li>&lt;strong>Global (Ruang Kerja Bersama)&lt;/strong>: Gunakan &lt;code>merge --no-ff&lt;/code> untuk mencatat keberadaan cabang fitur sebagai komit merge pada DAG, sehingga memudahkan revert dan pelacakan.&lt;/li>
&lt;/ul>
&lt;p>Dengan memahami latar belakang matematis dan arsitektural, seperti struktur DAG dan mekanisme fungsi hash, perintah Git akan meningkat dari sekadar hafalan menjadi &amp;ldquo;desain riwayat yang memiliki tujuan&amp;rdquo;. Sambil mematuhi aturan emas rebase, pilihlah perintah yang paling tepat sesuai situasi untuk membangun riwayat komit yang bersih, mudah dibaca, dan mudah dipelihara bagi seluruh tim.&lt;/p></description></item><item><title>Membangun Pipeline CI/CD untuk Proyek C++ menggunakan GitHub Actions</title><link>http://kenji.blog/id/p/github-actions-cpp-cicd-pipeline/</link><pubDate>Sat, 12 Sep 2026 18:00:00 +0900</pubDate><guid>http://kenji.blog/id/p/github-actions-cpp-cicd-pipeline/</guid><description>&lt;img src="http://kenji.blog/p/github-actions-cpp-cicd-pipeline/img/eyecatch.jpg" alt="Featured image of post Membangun Pipeline CI/CD untuk Proyek C++ menggunakan GitHub Actions" />&lt;h1 id="panduan-lengkap-membangun-pipeline-cicd-untuk-proyek-c-menggunakan-github-actions">Panduan Lengkap: Membangun Pipeline CI/CD untuk Proyek C++ menggunakan GitHub Actions
&lt;/h1>&lt;p>Dalam paradigma pengembangan perangkat lunak modern, Integrasi Berkelanjutan (Continuous Integration: CI) dan Pengiriman/Penerapan Berkelanjutan (Continuous Delivery/Deployment: CD) adalah elemen penting untuk mempertahankan proses pengembangan yang gesit (agile) dan perangkat lunak yang berkualitas tinggi. Di antara sekian banyak bahasa pemrograman yang ada, membangun pipeline CI/CD di C++ melibatkan kesulitan dan kompleksitas tersendiri dibandingkan dengan bahasa lain (seperti Python, JavaScript, Go, dll.).&lt;/p>
&lt;p>Pada artikel ini, kita akan membahas secara sangat detail tentang cara memanfaatkan GitHub Actions untuk membangun pipeline CI/CD yang kuat dan praktis untuk proyek C++ dari awal. Kita akan mencakup setiap teknik praktis, mulai dari matrix build pada lintas platform (Windows, Linux, macOS), integrasi sistem build menggunakan CMake, pengujian otomatis menggunakan CTest, otomatisasi analisis statis dan dinamis, pengukuran cakupan (coverage), hingga pengiriman otomatis (delivery) binary terkompilasi melalui GitHub Releases.&lt;/p>
&lt;h2 id="1-signifikansi-dan-tantangan-khusus-cicd-pada-proyek-c">1. Signifikansi dan Tantangan Khusus CI/CD pada Proyek C++
&lt;/h2>&lt;p>Dalam pengembangan aplikasi Web atau yang menggunakan bahasa skrip, sering kali menguji atau membangun di atas sebuah kontainer Docker tunggal sudah cukup. Namun, C++ adalah bahasa yang dikompilasi secara native dan sangat bergantung pada arsitektur perangkat keras dan sistem operasi dari lingkungan eksekusinya.&lt;/p>
&lt;p>Berikut adalah tantangan utama yang dihadapi ketika menerapkan CI/CD ke dalam proyek C++:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Keberagaman Platform&lt;/strong>: API (Windows API, POSIX, dll.) berbeda-beda di setiap OS seperti Windows, Linux, dan macOS. Sudah menjadi hal yang biasa jika kode berfungsi di lingkungan lokal pengembang (misalnya macOS) tetapi gagal dikompilasi di Linux atau Windows.&lt;/li>
&lt;li>&lt;strong>Perbedaan Kompiler&lt;/strong>: Kompiler utama seperti Microsoft Visual C++ (MSVC), GNU Compiler Collection (GCC), dan Clang memiliki tingkat implementasi standar C++ (C++17, C++20, C++23), interpretasi, dan ketegasan peringatan yang berbeda.&lt;/li>
&lt;li>&lt;strong>Waktu Build&lt;/strong>: Pada proyek C++ berskala besar, bukan hal yang aneh jika build memakan waktu puluhan menit hingga berjam-jam. Di lingkungan CI, diperlukan strategi caching dan paralelisasi untuk melakukan build secara efisien dengan sumber daya komputasi yang terbatas.&lt;/li>
&lt;li>&lt;strong>Manajemen Dependensi&lt;/strong>: C++ tidak memiliki manajer paket standar mutlak seperti npm atau pip. Anda harus selalu menyelesaikan library dengan benar di lingkungan CI menggunakan alat seperti vcpkg, Conan, atau &lt;code>FetchContent&lt;/code> dari CMake.&lt;/li>
&lt;li>&lt;strong>Manajemen Memori dan Perilaku Tak Terdefinisi (Undefined Behavior)&lt;/strong>: Karena melibatkan operasi pointer dan manajemen memori manual, Anda perlu mengotomatiskan tidak hanya pengujian logika, tetapi juga deteksi kebocoran memori (memory leak) dan perilaku tak terdefinisi (Undefined Behavior).&lt;/li>
&lt;/ol>
&lt;p>Untuk mengatasi tantangan ini, GitHub Actions, yang dapat memprovisikan berbagai mesin virtual OS sesuai permintaan (on-demand) dan mendefinisikan alur kerja yang kompleks dengan kode (Configuration as Code), adalah solusi yang optimal.&lt;/p>
&lt;h2 id="2-gambaran-arsitektur-pipeline-cicd">2. Gambaran Arsitektur Pipeline CI/CD
&lt;/h2>&lt;p>Mari kita visualisasikan gambaran keseluruhan dari pipeline CI/CD yang akan kita bangun. Diagram urutan (sequence diagram) Mermaid berikut menunjukkan alur kerja dari push kode hingga rilis.&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant Dev as &amp;#34;Pengembang&amp;#34;
participant Repo as &amp;#34;Repositori GitHub&amp;#34;
participant Action as &amp;#34;GitHub Actions CI/CD&amp;#34;
participant Rel as &amp;#34;GitHub Releases&amp;#34;
Dev-&amp;gt;&amp;gt;Repo: &amp;#34;Push cabang / Buka PR&amp;#34;
Repo-&amp;gt;&amp;gt;Action: &amp;#34;Picu Alur Kerja CI&amp;#34;
activate Action
Action-&amp;gt;&amp;gt;Action: &amp;#34;Lint &amp;amp; Analisis Statis (Clang-Tidy)&amp;#34;
rect rgb(200, 220, 240)
note right of Action: &amp;#34;Matrix Build Lintas Platform&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;Build di Ubuntu (GCC/Clang)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;Build di Windows (MSVC)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;Build di macOS (Apple Clang)&amp;#34;
end
Action-&amp;gt;&amp;gt;Action: &amp;#34;Jalankan CTest (dengan ASAN/UBSAN)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;Buat Laporan Cakupan&amp;#34;
alt &amp;#34;Jika Tag di-push (mis., v1.0.0)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;Paketkan Binary dengan CPack&amp;#34;
Action-&amp;gt;&amp;gt;Rel: &amp;#34;Unggah ZIP/Tarball ke Release&amp;#34;
end
deactivate Action
Repo--&amp;gt;&amp;gt;Dev: &amp;#34;Laporkan Status CI (Berhasil/Gagal)&amp;#34;
&lt;/pre>
&lt;p>Dalam arsitektur ini, tahap Pull Request memberikan umpan balik yang cepat (analisis statis, build, dan pengujian), dan saat tag versi ditambahkan, pemaketan dan distribusi artefak akan dilakukan.&lt;/p>
&lt;h2 id="3-pengaturan-proyek-dengan-cmake-modern">3. Pengaturan Proyek dengan CMake Modern
&lt;/h2>&lt;p>Fondasi dari pipeline CI yang sangat baik adalah sistem build yang kuat. Kita akan menggunakan CMake, standar de-facto untuk C++. Di sini kita mengadopsi pendekatan berorientasi target yang dikenal sebagai &amp;ldquo;CMake Modern&amp;rdquo;.&lt;/p>
&lt;p>Asumsikan struktur direktori proyek adalah sebagai berikut:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">my_cpp_project/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── CMakeLists.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">├── src/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── main.cpp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ ├── calculator.cpp
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">│ └── calculator.h
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└── tests/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> ├── CMakeLists.txt
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> └── test_calculator.cpp
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Berikut adalah contoh konfigurasi file &lt;code>CMakeLists.txt&lt;/code> di tingkat root:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;span class="lnt">39
&lt;/span>&lt;span class="lnt">40
&lt;/span>&lt;span class="lnt">41
&lt;/span>&lt;span class="lnt">42
&lt;/span>&lt;span class="lnt">43
&lt;/span>&lt;span class="lnt">44
&lt;/span>&lt;span class="lnt">45
&lt;/span>&lt;span class="lnt">46
&lt;/span>&lt;span class="lnt">47
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">cmake_minimum_required&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">VERSION&lt;/span> &lt;span class="s">3.20&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">project&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyCppProject&lt;/span> &lt;span class="s">VERSION&lt;/span> &lt;span class="s">1.0.0&lt;/span> &lt;span class="s">LANGUAGES&lt;/span> &lt;span class="s">CXX&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Pengaturan Standar C++
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_STANDARD&lt;/span> &lt;span class="s">20&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_STANDARD_REQUIRED&lt;/span> &lt;span class="s">ON&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CMAKE_CXX_EXTENSIONS&lt;/span> &lt;span class="s">OFF&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="c"># Menonaktifkan ekstensi khusus kompiler untuk meningkatkan portabilitas
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Memperketat Peringatan Kompiler
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">set_project_warnings&lt;/span> &lt;span class="s">target_name&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MSVC&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">target_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">${&lt;/span>&lt;span class="nv">target_name&lt;/span>&lt;span class="o">}&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">/W4&lt;/span> &lt;span class="s">/WX&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">else&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">target_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">${&lt;/span>&lt;span class="nv">target_name&lt;/span>&lt;span class="o">}&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">-Wall&lt;/span> &lt;span class="s">-Wextra&lt;/span> &lt;span class="s">-Wpedantic&lt;/span> &lt;span class="s">-Werror&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endfunction&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Pembuatan Target Library
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_library&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CalculatorLib&lt;/span> &lt;span class="s">src/calculator.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_include_directories&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CalculatorLib&lt;/span> &lt;span class="s">PUBLIC&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">CMAKE_CURRENT_SOURCE_DIR&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="s">/src&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set_project_warnings&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CalculatorLib&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Pembuatan Target Executable
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">add_executable&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyApplication&lt;/span> &lt;span class="s">src/main.cpp&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">target_link_libraries&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyApplication&lt;/span> &lt;span class="s">PRIVATE&lt;/span> &lt;span class="s">CalculatorLib&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set_project_warnings&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">MyApplication&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Mengaktifkan Pengujian
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">enable_testing&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">add_subdirectory&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">tests&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Definisi Aturan Instalasi (untuk CPack)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">include&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">GNUInstallDirs&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">install&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">TARGETS&lt;/span> &lt;span class="s">MyApplication&lt;/span> &lt;span class="s">CalculatorLib&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">RUNTIME&lt;/span> &lt;span class="s">DESTINATION&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">CMAKE_INSTALL_BINDIR&lt;/span>&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">LIBRARY&lt;/span> &lt;span class="s">DESTINATION&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">CMAKE_INSTALL_LIBDIR&lt;/span>&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">ARCHIVE&lt;/span> &lt;span class="s">DESTINATION&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">CMAKE_INSTALL_LIBDIR&lt;/span>&lt;span class="o">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="c"># Pengaturan Pemaketan dengan CPack
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CPACK_PROJECT_NAME&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">PROJECT_NAME&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CPACK_PROJECT_VERSION&lt;/span> &lt;span class="o">${&lt;/span>&lt;span class="nv">PROJECT_VERSION&lt;/span>&lt;span class="o">}&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CPACK_GENERATOR&lt;/span> &lt;span class="s2">&amp;#34;ZIP;TGZ&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">WIN32&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CPACK_GENERATOR&lt;/span> &lt;span class="s2">&amp;#34;ZIP&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">include&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">CPack&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;strong>Poin Penting:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;code>CMAKE_CXX_EXTENSIONS OFF&lt;/code>: Mencegah ketergantungan pada fitur non-standar seperti ekstensi GNU dan memastikan kemampuan lintas platform (cross-platform).&lt;/li>
&lt;li>&lt;strong>Memperketat Peringatan (&lt;code>-Werror&lt;/code> / &lt;code>/WX&lt;/code>)&lt;/strong>: Secara paksa menjaga kualitas kode agar tetap tinggi dengan memperlakukan peringatan kompiler sebagai kesalahan (error) di lingkungan CI.&lt;/li>
&lt;li>&lt;strong>GNUInstallDirs&lt;/strong>: Secara otomatis menyelesaikan jalur instalasi standar untuk setiap OS (misalnya, &lt;code>/usr/local/bin&lt;/code> atau &lt;code>C:\Program Files&lt;/code>).&lt;/li>
&lt;/ul>
&lt;h2 id="4-dasar-dasar-github-actions-dan-strategi-matriks">4. Dasar-dasar GitHub Actions dan Strategi Matriks
&lt;/h2>&lt;p>GitHub Actions dikonfigurasikan menggunakan file YAML di dalam direktori &lt;code>.github/workflows/&lt;/code>.
Fitur paling kuat untuk proyek C++ adalah &amp;ldquo;Strategi Matriks (Matrix Strategy)&amp;rdquo;. Fitur ini memungkinkan Anda menghasilkan kombinasi OS dan kompiler secara dinamis dan menjalankannya secara paralel.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Picu Alur Kerja&amp;#34;] --&amp;gt; B[&amp;#34;Evaluasi Pekerjaan Matriks&amp;#34;]
B --&amp;gt; C[&amp;#34;Ubuntu 22.04 (GCC 12)&amp;#34;]
B --&amp;gt; D[&amp;#34;Ubuntu 22.04 (Clang 15)&amp;#34;]
B --&amp;gt; E[&amp;#34;Windows Server 2022 (MSVC)&amp;#34;]
B --&amp;gt; F[&amp;#34;macOS 14 (Apple Clang)&amp;#34;]
&lt;/pre>
&lt;p>Di bawah ini adalah contoh definisi tugas (job) YAML dasar untuk matrix build.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Build &amp;amp; Test [${{ matrix.os }} - ${{ matrix.compiler }}]&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.os }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">strategy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">fail-fast&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># Lanjutkan build di OS lain meskipun ada satu pekerjaan (job) yang gagal&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matrix&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">include&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">gcc&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">gcc&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">g++&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang++&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">windows-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">msvc&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cl&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cl&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">macos-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">apple-clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang++&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;code>fail-fast: false&lt;/code> sangatlah penting. Sebagai contoh, jika Anda tidak sengaja menggunakan API khusus Linux, build di Ubuntu akan gagal, tetapi Anda ingin memeriksa pada saat yang sama apakah build di Windows berhasil atau tidak.&lt;/p>
&lt;h2 id="5-mengoptimalkan-proses-paralel-menggunakan-hukum-amdahl-dan-biaya-build">5. Mengoptimalkan Proses Paralel Menggunakan Hukum Amdahl dan Biaya Build
&lt;/h2>&lt;p>CI/CD di lingkungan komputasi awan (cloud) adalah pertarungan melawan waktu, dan waktu build berhubungan langsung dengan waktu tunggu pengembang dan biaya operasional (running cost).
Mari kita lakukan pendekatan matematis untuk mengoptimalkan waktu build menggunakan &amp;ldquo;Hukum Amdahl (Amdahl&amp;rsquo;s Law)&amp;rdquo; dalam ilmu komputer.&lt;/p>
&lt;p>Hukum Amdahl mendefinisikan peningkatan kecepatan maksimum teoritis $S(N)$ saat menggunakan $N$ prosesor, dengan persentase bagian dari program yang dapat diparalelkan diasumsikan sebagai $P$, seperti berikut:&lt;/p>
$$ S(N) = \frac{1}{(1 - P) + \frac{P}{N}} $$&lt;p>Dalam proses build C++, kompilasi dari setiap unit terjemahan (Translation Unit: file &lt;code>.cpp&lt;/code>) dari kode sumber sepenuhnya independen dan dapat diparalelkan. Di sisi lain, konfigurasi CMake dan fase penautan (link) binary akhir pada dasarnya dieksekusi secara serial (tidak dapat diparalelkan).&lt;/p>
&lt;p>Misalkan, dari total waktu build proyek, 80% adalah fase kompilasi ($P = 0.8$) dan 20% adalah fase serial ($1 - P = 0.2$).
Runner standar GitHub Actions (Linux) menyediakan 2 core (utas/thread). Oleh karena itu, untuk $N = 2$:&lt;/p>
$$ S(2) = \frac{1}{0.2 + \frac{0.8}{2}} = \frac{1}{0.2 + 0.4} = \frac{1}{0.6} \approx 1.67 $$&lt;p>Hanya dengan menggunakan 2 core, kita bisa mendapatkan peningkatan kecepatan sekitar 1,67 kali lipat. Untuk mewujudkan hal ini, menentukan opsi &lt;code>--parallel&lt;/code> pada perintah build CMake adalah suatu keharusan.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Build Project&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cmake --build build --config Release --parallel 2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Selain itu, kita juga harus mempertimbangkan perhitungan biaya. Total biaya GitHub Actions $C_{total}$ adalah jumlah dari produk waktu eksekusi pekerjaan $T_i$ dan harga satuan runner $R_i$.&lt;/p>
$$ C_{total} = \sum_{i=1}^{M} \left( T_i \times R_i \right) $$&lt;p>Mengurangi waktu build tidak hanya mempercepat putaran umpan balik (feedback loop) tetapi juga secara langsung mengurangi biaya operasional proyek (terutama untuk repositori privat). Jika Anda menginginkan lebih banyak peningkatan kecepatan, memperkenalkan &lt;code>ccache&lt;/code> untuk men-cache (cache) hasil kompilasi adalah pendekatan yang efektif.&lt;/p>
&lt;h2 id="6-integrasi-pengujian-otomatis-dan-pembersih-sanitizers">6. Integrasi Pengujian Otomatis dan Pembersih (Sanitizers)
&lt;/h2>&lt;p>Untuk mencegah bug pada C++ sebelum terjadi, sangat disarankan untuk menggunakan &amp;ldquo;Sanitizers&amp;rdquo; (Pembersih) yang dapat mendeteksi kebocoran memori dan perilaku tak terdefinisi pada saat eksekusi (runtime), selain pengujian unit (unit test). Kita akan menggunakan AddressSanitizer (ASAN) dan UndefinedBehaviorSanitizer (UBSAN) yang dikembangkan oleh Google.&lt;/p>
&lt;p>Tambahkan opsi untuk mengaktifkan sanitizers di CMake.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">option&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ENABLE_SANITIZERS&lt;/span> &lt;span class="s2">&amp;#34;Enable ASAN and UBSAN&amp;#34;&lt;/span> &lt;span class="s">OFF&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ENABLE_SANITIZERS&lt;/span> &lt;span class="s">AND&lt;/span> &lt;span class="s">CMAKE_CXX_COMPILER_ID&lt;/span> &lt;span class="s">MATCHES&lt;/span> &lt;span class="s2">&amp;#34;GNU|Clang&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">-fsanitize=address,undefined&lt;/span> &lt;span class="s">-fno-omit-frame-pointer&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_link_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">-fsanitize=address,undefined&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Aktifkan opsi ini dan jalankan pengujian dalam tugas (job) Ubuntu dari pipeline CI.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Configure CMake&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CC&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.c_compiler }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CXX&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.cpp_compiler }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> cmake -B build
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> -DCMAKE_BUILD_TYPE=Release
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> -DENABLE_SANITIZERS=${{ matrix.os == &amp;#39;ubuntu-latest&amp;#39; &amp;amp;&amp;amp; &amp;#39;ON&amp;#39; || &amp;#39;OFF&amp;#39; }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Run CTest&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">working-directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ASAN_OPTIONS&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;detect_leaks=1:symbolize=1&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">UBSAN_OPTIONS&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;print_stacktrace=1&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ctest --build-config Release --output-on-failure --parallel 2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Gunakan perintah &lt;code>ctest&lt;/code> untuk menjalankan pengujian. Dengan menentukan &lt;code>--output-on-failure&lt;/code>, hanya detail log dari pengujian yang gagal yang akan ditampilkan pada output CI, mencegah log menjadi terlalu membengkak.&lt;/p>
&lt;h2 id="7-pengukuran-cakupan-code-coverage">7. Pengukuran Cakupan (Code Coverage)
&lt;/h2>&lt;p>Memvisualisasikan seberapa banyak kode yang tercakup (diuji) oleh tes (pengujian) sangat penting untuk jaminan kualitas. Menggunakan lingkungan Linux (GCC), kita mengukur cakupan dengan &lt;code>gcov&lt;/code> dan &lt;code>lcov&lt;/code>.&lt;/p>
&lt;p>Pertama, tetapkan tanda (flag) kompilasi untuk pengukuran cakupan di CMake.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-cmake" data-lang="cmake">&lt;span class="line">&lt;span class="cl">&lt;span class="nb">option&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ENABLE_COVERAGE&lt;/span> &lt;span class="s2">&amp;#34;Enable coverage reporting&amp;#34;&lt;/span> &lt;span class="s">OFF&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">ENABLE_COVERAGE&lt;/span> &lt;span class="s">AND&lt;/span> &lt;span class="s">CMAKE_CXX_COMPILER_ID&lt;/span> &lt;span class="s">STREQUAL&lt;/span> &lt;span class="s2">&amp;#34;GNU&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_compile_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">--coverage&lt;/span> &lt;span class="s">-O0&lt;/span> &lt;span class="s">-g&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span> &lt;span class="nb">add_link_options&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">--coverage&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="nb">endif&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Tentukan tugas (job) terpisah untuk pengukuran cakupan di GitHub Actions.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">coverage&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Test Coverage Analysis&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Install lcov&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install -y lcov&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Configure CMake for Coverage&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cmake -B build -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=ON&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Build &amp;amp; Test&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> cmake --build build --parallel 2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> cd build &amp;amp;&amp;amp; ctest --output-on-failure&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Generate lcov Report&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">working-directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> lcov --capture --directory . --output-file coverage.info
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> lcov --remove coverage.info &amp;#39;/usr/*&amp;#39; &amp;#39;*_deps/*&amp;#39; &amp;#39;*tests/*&amp;#39; --output-file coverage.info
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> &lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Upload Coverage to Codecov&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">codecov/codecov-action@v3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">files&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build/coverage.info&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Menggunakan perintah &lt;code>lcov --remove&lt;/code>, sistem header, library pihak ketiga, dan kode pengujian itu sendiri dikecualikan dari pengukuran cakupan. Dengan cara ini, Anda bisa mendapatkan cakupan murni hanya untuk kode sumber spesifik milik proyek.&lt;/p>
&lt;h2 id="8-pengiriman-otomatis-cd-binary-melalui-github-releases">8. Pengiriman Otomatis (CD) Binary melalui GitHub Releases
&lt;/h2>&lt;p>Mari kita bangun bagian &amp;ldquo;CD&amp;rdquo; (Continuous Delivery) dari pipeline CI/CD. Ketika pengembang menambahkan tag versi (misalnya &lt;code>v1.2.0&lt;/code>) di Git dan melakukan push, binary yang dapat dieksekusi (executable) untuk setiap OS akan dikompilasi secara otomatis, dikemas menjadi file ZIP atau Tarball, dan diunggah ke GitHub Releases.&lt;/p>
&lt;p>Pada langkah ini, kita menggunakan alat pemaketan (packaging tool) &lt;code>CPack&lt;/code> yang disertakan di dalam CMake.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Package Application (CPack)&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">if&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">startsWith(github.ref, &amp;#39;refs/tags/v&amp;#39;)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">working-directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cpack -C Release -V&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Upload Release Assets&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">if&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">startsWith(github.ref, &amp;#39;refs/tags/v&amp;#39;)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">softprops/action-gh-release@v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">files&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> build/*.tar.gz
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> build/*.zip
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> build/*.sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> build/*.exe&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">GITHUB_TOKEN&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.GITHUB_TOKEN }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dengan konfigurasi ini, cukup dengan mengeksekusi &lt;code>git tag v1.0.0&lt;/code> dan &lt;code>git push origin v1.0.0&lt;/code>, pengguna Windows akan mendapatkan file ZIP, dan pengguna Linux/macOS akan mendapatkan file Tarball, yang secara otomatis dipublikasikan ke halaman rilis tanpa campur tangan manual. Ini adalah fitur yang sangat hebat untuk mendistribusikan perangkat lunak kepada pengguna.&lt;/p>
&lt;h2 id="9-file-yaml-workflow-lengkap">9. File YAML Workflow Lengkap
&lt;/h2>&lt;p>Di bawah ini adalah kode lengkap untuk &lt;code>.github/workflows/main.yml&lt;/code> yang kuat dan praktis, yang mengintegrasikan semua elemen yang telah dibahas sebelumnya.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt"> 10
&lt;/span>&lt;span class="lnt"> 11
&lt;/span>&lt;span class="lnt"> 12
&lt;/span>&lt;span class="lnt"> 13
&lt;/span>&lt;span class="lnt"> 14
&lt;/span>&lt;span class="lnt"> 15
&lt;/span>&lt;span class="lnt"> 16
&lt;/span>&lt;span class="lnt"> 17
&lt;/span>&lt;span class="lnt"> 18
&lt;/span>&lt;span class="lnt"> 19
&lt;/span>&lt;span class="lnt"> 20
&lt;/span>&lt;span class="lnt"> 21
&lt;/span>&lt;span class="lnt"> 22
&lt;/span>&lt;span class="lnt"> 23
&lt;/span>&lt;span class="lnt"> 24
&lt;/span>&lt;span class="lnt"> 25
&lt;/span>&lt;span class="lnt"> 26
&lt;/span>&lt;span class="lnt"> 27
&lt;/span>&lt;span class="lnt"> 28
&lt;/span>&lt;span class="lnt"> 29
&lt;/span>&lt;span class="lnt"> 30
&lt;/span>&lt;span class="lnt"> 31
&lt;/span>&lt;span class="lnt"> 32
&lt;/span>&lt;span class="lnt"> 33
&lt;/span>&lt;span class="lnt"> 34
&lt;/span>&lt;span class="lnt"> 35
&lt;/span>&lt;span class="lnt"> 36
&lt;/span>&lt;span class="lnt"> 37
&lt;/span>&lt;span class="lnt"> 38
&lt;/span>&lt;span class="lnt"> 39
&lt;/span>&lt;span class="lnt"> 40
&lt;/span>&lt;span class="lnt"> 41
&lt;/span>&lt;span class="lnt"> 42
&lt;/span>&lt;span class="lnt"> 43
&lt;/span>&lt;span class="lnt"> 44
&lt;/span>&lt;span class="lnt"> 45
&lt;/span>&lt;span class="lnt"> 46
&lt;/span>&lt;span class="lnt"> 47
&lt;/span>&lt;span class="lnt"> 48
&lt;/span>&lt;span class="lnt"> 49
&lt;/span>&lt;span class="lnt"> 50
&lt;/span>&lt;span class="lnt"> 51
&lt;/span>&lt;span class="lnt"> 52
&lt;/span>&lt;span class="lnt"> 53
&lt;/span>&lt;span class="lnt"> 54
&lt;/span>&lt;span class="lnt"> 55
&lt;/span>&lt;span class="lnt"> 56
&lt;/span>&lt;span class="lnt"> 57
&lt;/span>&lt;span class="lnt"> 58
&lt;/span>&lt;span class="lnt"> 59
&lt;/span>&lt;span class="lnt"> 60
&lt;/span>&lt;span class="lnt"> 61
&lt;/span>&lt;span class="lnt"> 62
&lt;/span>&lt;span class="lnt"> 63
&lt;/span>&lt;span class="lnt"> 64
&lt;/span>&lt;span class="lnt"> 65
&lt;/span>&lt;span class="lnt"> 66
&lt;/span>&lt;span class="lnt"> 67
&lt;/span>&lt;span class="lnt"> 68
&lt;/span>&lt;span class="lnt"> 69
&lt;/span>&lt;span class="lnt"> 70
&lt;/span>&lt;span class="lnt"> 71
&lt;/span>&lt;span class="lnt"> 72
&lt;/span>&lt;span class="lnt"> 73
&lt;/span>&lt;span class="lnt"> 74
&lt;/span>&lt;span class="lnt"> 75
&lt;/span>&lt;span class="lnt"> 76
&lt;/span>&lt;span class="lnt"> 77
&lt;/span>&lt;span class="lnt"> 78
&lt;/span>&lt;span class="lnt"> 79
&lt;/span>&lt;span class="lnt"> 80
&lt;/span>&lt;span class="lnt"> 81
&lt;/span>&lt;span class="lnt"> 82
&lt;/span>&lt;span class="lnt"> 83
&lt;/span>&lt;span class="lnt"> 84
&lt;/span>&lt;span class="lnt"> 85
&lt;/span>&lt;span class="lnt"> 86
&lt;/span>&lt;span class="lnt"> 87
&lt;/span>&lt;span class="lnt"> 88
&lt;/span>&lt;span class="lnt"> 89
&lt;/span>&lt;span class="lnt"> 90
&lt;/span>&lt;span class="lnt"> 91
&lt;/span>&lt;span class="lnt"> 92
&lt;/span>&lt;span class="lnt"> 93
&lt;/span>&lt;span class="lnt"> 94
&lt;/span>&lt;span class="lnt"> 95
&lt;/span>&lt;span class="lnt"> 96
&lt;/span>&lt;span class="lnt"> 97
&lt;/span>&lt;span class="lnt"> 98
&lt;/span>&lt;span class="lnt"> 99
&lt;/span>&lt;span class="lnt">100
&lt;/span>&lt;span class="lnt">101
&lt;/span>&lt;span class="lnt">102
&lt;/span>&lt;span class="lnt">103
&lt;/span>&lt;span class="lnt">104
&lt;/span>&lt;span class="lnt">105
&lt;/span>&lt;span class="lnt">106
&lt;/span>&lt;span class="lnt">107
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;C++ CI/CD Pipeline&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">push&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;main&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;develop&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">tags&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;v*.*.*&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">pull_request&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;main&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">BUILD_TYPE&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Release&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build-and-test&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Build [${{ matrix.os }} | ${{ matrix.compiler }}]&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.os }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">strategy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">fail-fast&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">matrix&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">include&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">gcc&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">gcc&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">g++&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang++&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">windows-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">msvc&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cl&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cl&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">os&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">macos-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">apple-clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">c_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">cpp_compiler&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">clang++&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Checkout Repository&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Configure CMake&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CC&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.c_compiler }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">CXX&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ matrix.cpp_compiler }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">&amp;gt;&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> cmake -B build
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> -DENABLE_SANITIZERS=${{ matrix.os == &amp;#39;ubuntu-latest&amp;#39; &amp;amp;&amp;amp; &amp;#39;ON&amp;#39; || &amp;#39;OFF&amp;#39; }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Build Project&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cmake --build build --config ${{ env.BUILD_TYPE }} --parallel 2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Run Unit Tests (CTest)&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">working-directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ASAN_OPTIONS&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;detect_leaks=1:symbolize=1&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">UBSAN_OPTIONS&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;print_stacktrace=1&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ctest --build-config ${{ env.BUILD_TYPE }} --output-on-failure --parallel 2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Package with CPack&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">if&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">startsWith(github.ref, &amp;#39;refs/tags/v&amp;#39;)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">working-directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cpack -C ${{ env.BUILD_TYPE }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Create GitHub Release and Upload Assets&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">if&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">startsWith(github.ref, &amp;#39;refs/tags/v&amp;#39;)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">softprops/action-gh-release@v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">files&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> build/*.tar.gz
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> build/*.zip&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">GITHUB_TOKEN&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.GITHUB_TOKEN }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">coverage&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Code Coverage Analysis&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">if&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">github.event_name == &amp;#39;pull_request&amp;#39; || github.ref == &amp;#39;refs/heads/main&amp;#39;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Checkout Repository&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Install lcov&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install -y lcov&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Configure CMake for Coverage&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cmake -B build -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=ON&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Build Project&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cmake --build build --parallel 2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Run Tests&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">working-directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ctest --output-on-failure&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Generate lcov Report&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">working-directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> lcov --capture --directory . --output-file coverage.info
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> lcov --remove coverage.info &amp;#39;/usr/*&amp;#39; &amp;#39;*_deps/*&amp;#39; &amp;#39;*tests/*&amp;#39; --output-file coverage.info
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="sd"> &lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Upload Coverage to Codecov&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">codecov/codecov-action@v3&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">files&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">build/coverage.info&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">fail_ci_if_error&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">false&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h2 id="10-menuju-cicd-yang-lebih-lanjut-analisis-statis-dan-pemformatan">10. Menuju CI/CD yang Lebih Lanjut (Analisis Statis dan Pemformatan)
&lt;/h2>&lt;p>Meskipun kita melewatkan penjelasan rinci di sini, sangat disarankan untuk memasukkan lebih banyak alat jaminan kualitas ke dalam pipeline pada penggunaan praktis (production).&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Memaksakan Clang-Format&lt;/strong>: Untuk mengurangi beban ulasan (review) kode, integrasikan pengecekan gaya penulisan kode menggunakan &lt;code>clang-format&lt;/code> ke dalam CI, dan buat pipeline gagal jika melanggar aturan format.&lt;/li>
&lt;li>&lt;strong>Analisis Statis (Clang-Tidy)&lt;/strong>: Untuk mendeteksi bug tersembunyi yang tidak dapat dicegah oleh peringatan kompiler saja, atau kode yang tidak efisien (seperti penyalinan yang tidak perlu), integrasikan &lt;code>clang-tidy&lt;/code> ke dalam CMake dan jalankan di atas CI.&lt;/li>
&lt;li>&lt;strong>Memanfaatkan Cache vcpkg / Conan&lt;/strong>: Saat menggunakan banyak library pihak ketiga, membangun dependensi memakan banyak waktu. Memanfaatkan &lt;code>actions/cache&lt;/code> dari GitHub Actions untuk menyimpan direktori instalasi vcpkg atau cache Conan dapat secara drastis mengurangi waktu build.&lt;/li>
&lt;/ol>
&lt;h2 id="kesimpulan">Kesimpulan
&lt;/h2>&lt;p>Membangun pipeline CI/CD untuk proyek C++ sekilas mungkin tampak sangat sulit karena ketergantungan pada platform dan kompleksitas alat build-nya. Namun, dengan menggabungkan ekosistem GitHub Actions, CMake Modern, dan CTest/CPack dengan benar, Anda bisa mendapatkan alur pengembangan (workflow) yang sangat kuat dan otomatis.&lt;/p>
&lt;p>Validasi lintas platform (cross-platform) menggunakan strategi matriks, deteksi bug pada saat runtime (waktu eksekusi) menggunakan pembersih (sanitizers), pengukuran cakupan (code coverage), dan penerapan otomatis (auto deployment) ke GitHub Releases yang dibahas dalam artikel ini adalah praktik terbaik yang banyak diadopsi bahkan dalam proyek open-source komersial.&lt;/p>
&lt;p>Pipeline CI/CD otomatis meminimalkan waktu yang dihabiskan pengembang untuk &amp;ldquo;mencari bug&amp;rdquo; atau &amp;ldquo;tugas build dan rilis manual&amp;rdquo;, menjadikannya senjata yang luar biasa untuk membantu mereka fokus pada kegiatan pembuatan kode yang sebenarnya (coding kreatif). Jangan ragu untuk menerapkannya pada proyek C++ Anda sendiri demi mencapai kehidupan pengembangan yang lebih lincah dan bebas dari rasa khawatir.&lt;/p></description></item><item><title>Cara Menghosting Blog Hugo Secara Gratis dan Super Cepat di Cloudflare Pages atau GitHub Pages</title><link>http://kenji.blog/id/p/hugo-hosting-cloudflare-vs-github-pages/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/id/p/hugo-hosting-cloudflare-vs-github-pages/</guid><description>&lt;img src="http://kenji.blog/p/hugo-hosting-cloudflare-vs-github-pages/img/eyecatch.jpg" alt="Featured image of post Cara Menghosting Blog Hugo Secara Gratis dan Super Cepat di Cloudflare Pages atau GitHub Pages" />&lt;p>Dalam mengelola situs web atau blog, kecepatan pemuatan (kinerja), biaya operasional, dan keamanan adalah faktor yang sangat penting. Di masa lalu, kombinasi CMS (Content Management System) dinamis seperti WordPress dan server sewaan adalah yang utama, tetapi saat ini, arsitektur yang disebut &amp;ldquo;Jamstack&amp;rdquo; menarik banyak perhatian. Di antaranya, menggabungkan &amp;ldquo;Hugo&amp;rdquo;, generator situs statis (SSG) super cepat yang dibuat dalam bahasa Go, dengan layanan hosting modern seperti Cloudflare Pages atau GitHub Pages memungkinkan Anda membangun lingkungan blog yang &lt;strong>sepenuhnya gratis dan sangat cepat&lt;/strong>.&lt;/p>
&lt;p>Artikel ini akan membahas secara mendalam dari perspektif teknis langkah-langkah spesifik untuk memublikasikan situs statis berbasis Hugo di Cloudflare Pages atau GitHub Pages, perbedaan arsitektur di setiap platform, pengaturan CI/CD (Continuous Integration / Continuous Deployment) menggunakan GitHub Actions, pengoptimalan DNS, strategi cache, hingga penerapan analisis akses yang mengutamakan privasi.&lt;/p>
&lt;hr>
&lt;h2 id="1-dasar-dasar-generator-situs-statis-ssg-dan-jamstack">1. Dasar-dasar Generator Situs Statis (SSG) dan Jamstack
&lt;/h2>&lt;h3 id="11-mengapa-situs-statis">1.1 Mengapa Situs Statis?
&lt;/h3>&lt;p>CMS dinamis konvensional (misalnya WordPress) menjalankan kueri ke basis data (seperti MySQL) setiap kali ada permintaan dari pengguna, kemudian secara dinamis menghasilkan HTML di sisi server (seperti PHP) dan mengembalikannya. Metode ini menawarkan fleksibilitas tinggi, namun kurang tahan terhadap lonjakan lalu lintas yang tiba-tiba (seperti saat viral atau serangan DDoS), dan arsitektur infrastrukturnya cenderung menjadi rumit karena memerlukan server cache (seperti Redis atau Varnish) di bagian depan.&lt;/p>
&lt;p>Sebaliknya, Generator Situs Statis (SSG) yang mengadopsi arsitektur Jamstack (JavaScript, APIs, and Markup) akan menghasilkan (saat proses build) seluruh file HTML, CSS, dan JavaScript terlebih dahulu. Untuk setiap permintaan pengguna, server web (atau CDN) hanya akan mengembalikan file statis yang sudah dihasilkan tersebut, sehingga mampu memberikan kecepatan luar biasa dan keamanan yang kuat.&lt;/p>
&lt;h3 id="12-keunggulan-hugo">1.2 Keunggulan Hugo
&lt;/h3>&lt;p>Ada berbagai pilihan SSG seperti Next.js, Gatsby, Jekyll, dan Astro, namun fitur utama Hugo adalah &lt;strong>kecepatan build-nya&lt;/strong>. Berkat pemrosesan konkurensi (pemrosesan paralel) dari bahasa Go, build dapat diselesaikan hanya dalam beberapa detik meskipun situs tersebut memiliki ribuan hingga puluhan ribu halaman. Hal ini secara signifikan mengurangi waktu tunggu pada pipeline CI/CD dan secara langsung meningkatkan pengalaman developer (DX: Developer Experience).&lt;/p>
&lt;hr>
&lt;h2 id="2-perbandingan-arsitektur-layanan-hosting">2. Perbandingan Arsitektur Layanan Hosting
&lt;/h2>&lt;p>Tantangan selanjutnya adalah di mana Anda akan menghosting file statis yang dihasilkan oleh Hugo. Pilihan yang umum di antaranya adalah Cloudflare Pages, GitHub Pages, dan Netlify, masing-masing memiliki arsitektur jaringan yang berbeda di balik layarnya.&lt;/p>
&lt;h3 id="21-cdn-dan-komputasi-edge-edge-computing">2.1 CDN dan Komputasi Edge (Edge Computing)
&lt;/h3>&lt;p>Semua platform ini mendistribusikan konten menggunakan CDN (Content Delivery Network) yang tersebar secara global. Namun, faktor pembedanya bukanlah sekadar menyimpan cache file statis, melainkan apakah mereka mampu mengeksekusi routing permintaan atau memodifikasi header di PoP (Point of Presence) terdekat dengan pengguna melalui &amp;ldquo;komputasi edge&amp;rdquo;.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Pengguna (Tokyo)&amp;#34;] --&amp;gt;|Latensi Rendah| B[&amp;#34;Node Edge (NRT)&amp;#34;]
C[&amp;#34;Pengguna (New York)&amp;#34;] --&amp;gt;|Latensi Rendah| D[&amp;#34;Node Edge (EWR)&amp;#34;]
E[&amp;#34;Pengguna (London)&amp;#34;] --&amp;gt;|Latensi Rendah| F[&amp;#34;Node Edge (LHR)&amp;#34;]
B --&amp;gt; G[&amp;#34;Penyimpanan Origin / Object Store&amp;#34;]
D --&amp;gt; G
F --&amp;gt; G
&lt;/pre>
&lt;h3 id="22-github-pages">2.2 GitHub Pages
&lt;/h3>&lt;p>GitHub Pages adalah layanan yang memungkinkan Anda mempublikasikan file HTML, CSS, dan JavaScript langsung dari repositori GitHub. Di belakangnya digunakan CDN seperti Fastly, yang memberikan kinerja yang memadai. Namun, fungsi infrastruktur murninya agak terbatas, karena terdapat batasan dalam menyesuaikan header (misalnya mengatur &lt;code>Cache-Control&lt;/code> atau header keamanan), dan pengaturan pengalihan (redirect) bergantung pada meta refresh HTML atau plugin Jekyll.&lt;/p>
&lt;h3 id="23-cloudflare-pages">2.3 Cloudflare Pages
&lt;/h3>&lt;p>Cloudflare Pages adalah layanan hosting situs statis yang dibangun di atas jaringan Anycast berskala global milik Cloudflare (tersebar di lebih dari 275 kota).
Layanan ini menawarkan penyesuaian kinerja yang luar biasa, termasuk dukungan standar untuk HTTP/3 (QUIC), optimasi gambar, dan integrasi dengan fungsi edge (Cloudflare Workers). Selain itu, tidak ada biaya bandwidth, sehingga keunggulan terbesarnya adalah Anda dapat menjalankannya secara gratis, tidak peduli seberapa besar lonjakan lalu lintas yang terjadi.&lt;/p>
&lt;h3 id="24-netlify">2.4 Netlify
&lt;/h3>&lt;p>Netlify adalah pionir di bidang Jamstack, menawarkan DX (Developer Experience) all-in-one yang mengintegrasikan fungsionalitas formulir, otentikasi (Identity), hingga fungsi serverless. Namun, Anda harus berhati-hati dengan manajemen biaya, karena apabila penggunaan bandwidth melebihi batas gratis (100GB per bulan), tagihan berdasarkan penggunaan dapat menjadi sangat mahal, terutama bagi blog yang banyak memuat gambar dan video.&lt;/p>
&lt;hr>
&lt;h2 id="3-perhitungan-teoretis-kinerja-dan-latensi-model-matematis-dengan-latex">3. Perhitungan Teoretis Kinerja dan Latensi (Model Matematis dengan LaTeX)
&lt;/h2>&lt;p>Dalam mengevaluasi kinerja web, mengurangi latensi (waktu tunda) adalah indikator terpenting. Mari kita buat model matematika tentang seberapa banyak latensi yang dikurangi saat menggunakan CDN (edge) dibandingkan dengan mengakses server origin secara langsung.&lt;/p>
&lt;p>Misalkan probabilitas permintaan pengguna untuk menemukan cache (Cache Hit Ratio) adalah $C$. Maka $0 \le C \le 1$.
Misalkan latensi ke server origin adalah $L_{origin}$, dan latensi ke node edge terdekat adalah $L_{edge}$.&lt;/p>
&lt;p>Rata-rata latensi baru $L_{new}$ dapat dihitung dengan nilai ekspektasi berikut:&lt;/p>
$$ L_{new} = C \times L_{edge} + (1 - C) \times (L_{edge} + L_{origin}) $$&lt;p>Jika disederhanakan, persamaannya menjadi:&lt;/p>
$$ L_{new} = L_{edge} + (1 - C) \times L_{origin} $$&lt;p>Sebagai contoh, ketika seorang pengguna di Tokyo mengakses server origin yang berada di pantai timur Amerika Serikat (New York), mengingat jarak fisik kabel serat optik dan latensi pemrosesan di router, $L_{origin}$ akan berkisar sekitar 200 md (milidetik). Sementara itu, jika menggunakan CDN seperti Cloudflare, karena pengguna akan terhubung ke node edge di Tokyo, $L_{edge}$ akan berkurang menjadi sekitar 10 md.&lt;/p>
&lt;p>Jika kita asumsikan rasio cache hit $C = 0.95$ (95%), maka:&lt;/p>
$$ L_{new} = 10 + (1 - 0.95) \times 200 = 10 + 0.05 \times 200 = 10 + 10 = 20 \text{ ms} $$&lt;p>Dengan demikian, pengenalan CDN dapat secara dramatis mengurangi (sekitar 90%) rata-rata latensi dari 210 md menjadi 20 md.&lt;/p>
&lt;hr>
&lt;h2 id="4-membangun-pipeline-cicd-dengan-github-actions">4. Membangun Pipeline CI/CD dengan GitHub Actions
&lt;/h2>&lt;p>Untuk mengotomatiskan proses pembaruan blog Hugo, kita akan membangun pipeline CI/CD menggunakan GitHub Actions. Melalui pengaturan ini, cukup dengan menulis artikel Markdown di lokal dan menjalankan &lt;code>git push&lt;/code>, proses build akan berjalan otomatis dan langsung di-deploy ke Cloudflare Pages atau GitHub Pages.&lt;/p>
&lt;p>Diagram urutan (sequence diagram) berikut ini menunjukkan alur keseluruhan dari mulai mem-push artikel hingga konten tersebut disampaikan kepada pengguna.&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant U as &amp;#34;Pengguna (Penulis)&amp;#34;
participant G as &amp;#34;Repositori GitHub&amp;#34;
participant A as &amp;#34;GitHub Actions&amp;#34;
participant C as &amp;#34;Cloudflare Pages&amp;#34;
participant V as &amp;#34;Pengunjung&amp;#34;
U-&amp;gt;&amp;gt;G: &amp;#34;git push origin main&amp;#34;
G-&amp;gt;&amp;gt;A: &amp;#34;Memicu Event Push&amp;#34;
A-&amp;gt;&amp;gt;A: &amp;#34;Checkout &amp;amp; Persiapan Hugo&amp;#34;
A-&amp;gt;&amp;gt;A: &amp;#34;hugo --minify (Proses Build)&amp;#34;
A-&amp;gt;&amp;gt;C: &amp;#34;Deploy ke Cloudflare Pages (Direct Upload)&amp;#34;
C--&amp;gt;&amp;gt;A: &amp;#34;URL Deployment (Sukses)&amp;#34;
V-&amp;gt;&amp;gt;C: &amp;#34;Permintaan Halaman Web&amp;#34;
C--&amp;gt;&amp;gt;V: &amp;#34;Mengembalikan HTML Ber-cache (Edge)&amp;#34;
&lt;/pre>
&lt;h3 id="41-konfigurasi-deployment-untuk-cloudflare-pages-direct-upload">4.1 Konfigurasi Deployment untuk Cloudflare Pages (Direct Upload)
&lt;/h3>&lt;p>Untuk Cloudflare Pages, terdapat dua metode: menghubungkan repositori GitHub dan membangunnya di infrastruktur Cloudflare, atau melakukan &amp;ldquo;Direct Upload (Unggah Langsung)&amp;rdquo; dari file statis yang di-build melalui GitHub Actions. Jika Anda ingin mengontrol versi Hugo lebih ketat dan mengintegrasikannya dengan pekerjaan lain (seperti pengujian atau optimasi gambar), disarankan untuk menggunakan metode Direct Upload setelah melakukan build di GitHub Actions.&lt;/p>
&lt;p>Berikut adalah contoh praktis file &lt;code>.github/workflows/deploy.yml&lt;/code> untuk deployment ke Cloudflare Pages.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;span class="lnt">15
&lt;/span>&lt;span class="lnt">16
&lt;/span>&lt;span class="lnt">17
&lt;/span>&lt;span class="lnt">18
&lt;/span>&lt;span class="lnt">19
&lt;/span>&lt;span class="lnt">20
&lt;/span>&lt;span class="lnt">21
&lt;/span>&lt;span class="lnt">22
&lt;/span>&lt;span class="lnt">23
&lt;/span>&lt;span class="lnt">24
&lt;/span>&lt;span class="lnt">25
&lt;/span>&lt;span class="lnt">26
&lt;/span>&lt;span class="lnt">27
&lt;/span>&lt;span class="lnt">28
&lt;/span>&lt;span class="lnt">29
&lt;/span>&lt;span class="lnt">30
&lt;/span>&lt;span class="lnt">31
&lt;/span>&lt;span class="lnt">32
&lt;/span>&lt;span class="lnt">33
&lt;/span>&lt;span class="lnt">34
&lt;/span>&lt;span class="lnt">35
&lt;/span>&lt;span class="lnt">36
&lt;/span>&lt;span class="lnt">37
&lt;/span>&lt;span class="lnt">38
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Deploy Hugo site to Cloudflare Pages&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">push&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branches&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="s2">&amp;#34;main&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">workflow_dispatch&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">build-and-deploy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;ubuntu-latest&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Checkout repository&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;actions/checkout@v4&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">submodules&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;recursive&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">fetch-depth&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">0&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Setup Hugo&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;peaceiris/actions-hugo@v3&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">hugo-version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;0.125.0&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">extended&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kc">true&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Build Hugo Site&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;hugo --minify --gc&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">env&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">HUGO_ENVIRONMENT&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;production&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;Deploy to Cloudflare Pages&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;cloudflare/pages-action@v1&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">with&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">apiToken&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.CLOUDFLARE_API_TOKEN }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">accountId&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.CLOUDFLARE_ACCOUNT_ID }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">projectName&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;nama-proyek-anda&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">directory&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;public&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">gitHubToken&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">${{ secrets.GITHUB_TOKEN }}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">branch&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;main&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dalam pipeline ini, opsi &lt;code>--minify&lt;/code> meminimalkan HTML/CSS/JS, dan &lt;code>--gc&lt;/code> menghapus file yang tidak diperlukan. Ini merupakan langkah dasar dalam pengoptimalan kinerja.&lt;/p>
&lt;hr>
&lt;h2 id="5-pendalaman-konfigurasi-dns-domain-kustom-dan-rekaman-cname--alias">5. Pendalaman Konfigurasi DNS: Domain Kustom dan Rekaman CNAME / ALIAS
&lt;/h2>&lt;p>Jika Anda menggunakan domain sendiri (kustom) (misalnya: &lt;code>kenji.blog&lt;/code>), pengaturan DNS (Domain Name System) yang tepat sangatlah penting.&lt;/p>
&lt;h3 id="51-keterbatasan-rekaman-cname-dan-zone-apex">5.1 Keterbatasan Rekaman CNAME dan Zone Apex
&lt;/h3>&lt;p>Secara umum, ketika Anda mengarahkan subdomain (misalnya &lt;code>www.kenji.blog&lt;/code>) ke layanan eksternal, Anda menggunakan rekaman &lt;code>CNAME&lt;/code>. Namun, menurut spesifikasi DNS (RFC 1034), Anda tidak dapat menetapkan rekaman &lt;code>CNAME&lt;/code> di root domain (sering disebut Zone Apex atau naked domain, contoh: &lt;code>kenji.blog&lt;/code>). Alasannya, di Zone Apex selalu harus ada rekaman SOA (Start of Authority), NS (Name Server), dan MX (Mail Exchange), dan ada aturan bahwa CNAME tidak dapat berdiri bersama dengan jenis rekaman sumber daya lainnya.&lt;/p>
&lt;h3 id="52-solusi-rekaman-alias--aname--cname-flattening">5.2 Solusi: Rekaman ALIAS / ANAME / CNAME Flattening
&lt;/h3>&lt;p>Untuk menyelesaikan masalah ini, penyedia DNS modern menawarkan fitur perluasan unik.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Rekaman ALIAS / ANAME&lt;/strong>: Server DNS secara dinamis memecahkan nama dan mengembalikan rekaman A (alamat IP) akhir ke klien. Amazon Route 53 dan layanan lainnya mendukung fitur ini.&lt;/li>
&lt;li>&lt;strong>CNAME Flattening&lt;/strong>: Ini adalah fitur yang disediakan oleh Cloudflare. Meskipun Anda mengatur CNAME di Zone Apex, server DNS otoritatif Cloudflare secara otomatis menyelesaikan alamat IP (rekaman A dan AAAA) dan mengembalikannya ke klien secara transparan seolah-olah menggunakan rekaman A.&lt;/li>
&lt;/ul>
&lt;p>Saat menggunakan Cloudflare Pages, mendelegasikan nameserver domain ke Cloudflare dan memanfaatkan &amp;ldquo;CNAME Flattening&amp;rdquo; ini adalah konfigurasi yang paling mulus dan berkinerja tinggi.&lt;/p>
&lt;hr>
&lt;h2 id="6-strategi-cache-dan-kontrol-header-http">6. Strategi Cache dan Kontrol Header HTTP
&lt;/h2>&lt;p>Selain itu, hal yang sangat penting untuk mempercepat situs statis adalah &amp;ldquo;strategi cache&amp;rdquo;. Di Cloudflare Pages, Anda dapat mengontrol header respons HTTP secara rinci dengan menggunakan file yang disediakan (file &lt;code>_headers&lt;/code>).&lt;/p>
&lt;h3 id="61-edge-cache-vs-browser-cache">6.1 Edge Cache vs Browser Cache
&lt;/h3>&lt;p>Secara umum, cache terbagi menjadi dua kategori: &amp;ldquo;Edge Cache&amp;rdquo; yang disimpan di sisi CDN, dan &amp;ldquo;Browser Cache&amp;rdquo; yang disimpan di peramban pengguna.&lt;/p>
&lt;p>Idealnya, file statis (seperti gambar, CSS, JS, yang nama filenya mengandung hash) harus disimpan dalam cache browser untuk jangka waktu yang lama. Di sisi lain, untuk file HTML, lazimnya cache browser dipersingkat (atau dinonaktifkan) dan diserahkan pengelolaannya kepada edge cache agar pembaruan konten dapat segera terlihat.&lt;/p>
&lt;p>Contoh konfigurasi &lt;code>_headers&lt;/code> pada Cloudflare Pages:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl"># File HTML tidak disimpan di cache browser, divalidasi setiap saat
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/*.html
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Cache-Control: public, max-age=0, must-revalidate
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"># File aset (CSS/JS/Gambar) disimpan di cache browser selama 1 tahun
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/assets/*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Cache-Control: public, max-age=31536000, immutable
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">/img/*
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Cache-Control: public, max-age=31536000, immutable
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="62-formula-perhitungan-pengurangan-biaya-bandwidth">6.2 Formula Perhitungan Pengurangan Biaya Bandwidth
&lt;/h3>&lt;p>Dengan mengatur header cache yang tepat, Anda dapat mengurangi jumlah transfer data dari server (edge) secara signifikan. Biaya bandwidth bulanan ($Cost$) dapat dinyatakan dengan model berikut, yang bergantung pada volume transfer tiap sumber daya $B_i$, rasio cache hit $C_i$, dan harga satuan bandwidth $R$.&lt;/p>
$$ Cost = \sum_{i=1}^{n} \left( B_i \times (1 - C_i) \times R \right) $$&lt;p>Karena lalu lintas keluar (egress) di Cloudflare gratis ($R = 0$), maka beban finansial secara langsung menjadi $0$. Akan tetapi, ketika menggunakan infrastruktur lain seperti GitHub Pages atau menggunakan backend seperti AWS S3, memaksimalkan rasio cache hit $C_i$ ini menjadi kunci dalam memangkas biaya infrastruktur.&lt;/p>
&lt;hr>
&lt;h2 id="7-analisis-akses-yang-menggabungkan-privasi-dan-kinerja">7. Analisis Akses yang Menggabungkan Privasi dan Kinerja
&lt;/h2>&lt;p>Dalam menjalankan blog, analisis akses (Web Analytics) untuk mengetahui seberapa banyak pengguna yang berkunjung sangatlah krusial. Google Analytics (GA4) sudah lama menjadi standar de facto, tetapi situasinya sedang berubah dengan maraknya perlindungan privasi beberapa tahun terakhir (seperti GDPR, CCPA) dan penghapusan Cookie pihak ketiga (Third-party Cookie).&lt;/p>
&lt;h3 id="71-dampaknya-terhadap-kinerja-web">7.1 Dampaknya Terhadap Kinerja Web
&lt;/h3>&lt;p>Menerapkan Google Analytics (khususnya &lt;code>gtag.js&lt;/code> atau Google Tag Manager) menyebabkan pemuatan dan pengeksekusian banyak skrip eksternal, yang berdampak negatif terhadap kinerja (terutama pada metrik TTFB dan waktu pemblokiran thread utama).&lt;/p>
&lt;p>Mari kita uraikan waktu pemuatan situs sebagai berikut:&lt;/p>
&lt;pre class="mermaid">
pie title &amp;#34;Rincian Waktu Pemuatan Situs Tipikal&amp;#34;
&amp;#34;TTFB (Time to First Byte)&amp;#34; : 15
&amp;#34;Unduhan Konten (HTML/CSS)&amp;#34; : 20
&amp;#34;Parsing &amp;amp; Rendering DOM&amp;#34; : 45
&amp;#34;Analitik &amp;amp; JS Pihak Ketiga&amp;#34; : 20
&lt;/pre>
&lt;p>Tidak jarang alat analisis JS pihak ketiga memakan sekitar 20% hingga 30% dari keseluruhan waktu pemuatan (loading).&lt;/p>
&lt;h3 id="72-implementasi-cloudflare-web-analytics">7.2 Implementasi Cloudflare Web Analytics
&lt;/h3>&lt;p>Oleh karena itu, alternatif yang menarik perhatian saat ini adalah menggunakan alat analitik yang mengutamakan privasi tanpa menggunakan Cookie (Cookieless), seperti Cloudflare Web Analytics atau Plausible Analytics.&lt;/p>
&lt;p>Cloudflare Web Analytics beroperasi hanya dengan menyematkan cuplikan (snippet) JavaScript yang sangat ringan, dan karena tidak menggunakan Cookie, Anda tidak perlu memasang banner persetujuan Cookie (Cookie Consent Banner) yang mengganggu.&lt;/p>
&lt;p>Implementasinya di Hugo juga sangat mudah. Cukup tambahkan cuplikan kode yang diberikan ke dalam &lt;code>layouts/partials/head.html&lt;/code> atau &lt;code>layouts/partials/analytics.html&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">{{ if eq hugo.Environment &amp;#34;production&amp;#34; }}
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- Cloudflare Web Analytics --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">defer&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#39;https://static.cloudflareinsights.com/beacon.min.js&amp;#39;&lt;/span> &lt;span class="na">data-cf-beacon&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#39;{&amp;#34;token&amp;#34;: &amp;#34;TOKEN_BEACON_CLOUDFLARE_ANDA&amp;#34;}&amp;#39;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&amp;lt;!-- End Cloudflare Web Analytics --&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">{{ end }}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dengan menambahkan atribut &lt;code>defer&lt;/code>, skrip akan dimuat secara asinkron tanpa memblokir proses parsing HTML, dan akan dieksekusi setelah pembuatan DOM selesai. Hal ini membantu meminimalkan dampak terhadap kecepatan tampilan awal (LCP: Largest Contentful Paint dan FCP: First Contentful Paint).&lt;/p>
&lt;hr>
&lt;h2 id="8-kesimpulan-dan-praktik-terbaik">8. Kesimpulan dan Praktik Terbaik
&lt;/h2>&lt;p>Dalam mengoperasikan situs statis dengan Hugo, menggunakan platform hosting modern seperti Cloudflare Pages atau GitHub Pages menawarkan manfaat luar biasa dalam hal efektivitas biaya, kecepatan tampilan, dan keamanan.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Build Super Cepat&lt;/strong>: Manfaatkan kecepatan Hugo untuk meminimalkan waktu eksekusi pipeline CI/CD (GitHub Actions).&lt;/li>
&lt;li>&lt;strong>Distribusi di Edge&lt;/strong>: Gunakan jaringan edge Cloudflare untuk mengirimkan konten kepada pengguna di seluruh dunia dengan latensi dalam hitungan milidetik.&lt;/li>
&lt;li>&lt;strong>Konfigurasi DNS yang Tepat&lt;/strong>: Manfaatkan CNAME Flattening untuk mengelola Zone Apex (domain kustom) secara aman dan cepat.&lt;/li>
&lt;li>&lt;strong>Optimasi Strategi Cache&lt;/strong>: Gunakan &lt;code>_headers&lt;/code> untuk memisahkan cache browser dan cache edge berdasarkan jenis sumber dayanya dengan tepat.&lt;/li>
&lt;li>&lt;strong>Analitik yang Ringan&lt;/strong>: Terapkan layanan seperti Cloudflare Web Analytics yang peduli terhadap privasi tanpa mengorbankan kinerja.&lt;/li>
&lt;/ol>
&lt;p>Dengan menggabungkan berbagai teknologi ini, Anda dapat membangun sistem blog yang kuat dan skalabel untuk menangani jutaan pengunjung (PV) setiap bulannya secara gratis. Bagi Anda yang sedang mempertimbangkan untuk meluncurkan blog teknis, situs perusahaan, atau situs portofolio, silakan coba kombinasi arsitektur Jamstack + Hugo + Cloudflare Pages ini.&lt;/p></description></item></channel></rss>