<?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/pt/categories/devops/</link><description>Recent content in DevOps on kenji.blog</description><generator>Hugo -- gohugo.io</generator><language>pt</language><copyright>kenjinote</copyright><lastBuildDate>Sun, 13 Sep 2026 09:00:00 +0900</lastBuildDate><atom:link href="http://kenji.blog/pt/categories/devops/index.xml" rel="self" type="application/rss+xml"/><item><title>【Comandos Git】A diferença entre rebase e merge e como usá-los corretamente na prática</title><link>http://kenji.blog/pt/p/git-rebase-vs-merge-practical-guide/</link><pubDate>Sun, 13 Sep 2026 09:00:00 +0900</pubDate><guid>http://kenji.blog/pt/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 【Comandos Git】A diferença entre rebase e merge e como usá-los corretamente na prática" />&lt;h1 id="1-introdução-por-que-merge-ou-rebase-é-um-dilema-eterno">1. Introdução: Por que &amp;ldquo;merge ou rebase&amp;rdquo; é um dilema eterno
&lt;/h1>&lt;p>Git é um sistema de controle de versão indispensável no desenvolvimento de software moderno. Quando vários desenvolvedores alteram uma base de código simultaneamente, o poderoso modelo de branches do Git entra em ação. No entanto, no desenvolvimento em equipe, a discussão sobre &amp;ldquo;qual usar: &lt;code>merge&lt;/code> ou &lt;code>rebase&lt;/code>&amp;rdquo; é um tópico que sempre atormenta os desenvolvedores, de iniciantes a veteranos.&lt;/p>
&lt;p>Neste artigo, desvendaremos a diferença entre os mecanismos de &lt;code>git merge&lt;/code> e &lt;code>git rebase&lt;/code>, explorando a estrutura interna do Git, como o DAG (Grafo Acíclico Direcionado) e as propriedades matemáticas dos hashes de commit. Além disso, explicaremos detalhadamente como usá-los corretamente na prática, com fluxos de trabalho específicos. Ao compreender as operações matemáticas que o Git realiza nos bastidores, e não apenas introduzir comandos, você perderá o medo de conflitos e poderá construir um histórico limpo e rastreável.&lt;/p>
&lt;hr>
&lt;h1 id="2-estrutura-interna-do-git-hashes-de-commit-e-modelo-de-objetos">2. Estrutura interna do Git: Hashes de commit e modelo de objetos
&lt;/h1>&lt;p>Para entender como o Git integra o histórico, primeiro precisamos saber como ele armazena dados. O Git não armazena apenas as diferenças (patches) de alterações de arquivos, mas sim um instantâneo (snapshot) de todo o sistema de arquivos em um determinado momento.&lt;/p>
&lt;h2 id="21-propriedades-criptográficas-dos-hashes-de-commit">2.1 Propriedades criptográficas dos hashes de commit
&lt;/h2>&lt;p>Cada commit no Git é identificado de forma única por um número hexadecimal de 40 dígitos gerado pela função de hash SHA-1 (Secure Hash Algorithm 1), calculada com base no seu conteúdo. Um objeto de commit é composto pelos seguintes elementos:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Ponteiro para o objeto Tree&lt;/strong>: O instantâneo da estrutura de diretórios e arquivos (Blob) naquele momento&lt;/li>
&lt;li>&lt;strong>Ponteiro para o commit pai&lt;/strong>: O valor do hash de um ou mais commits pais (o primeiro commit não tem pai e um commit de merge tem dois ou mais pais)&lt;/li>
&lt;li>&lt;strong>Informações do autor (Author)&lt;/strong>: A pessoa que escreveu o código e a data/hora&lt;/li>
&lt;li>&lt;strong>Informações do committer (Committer)&lt;/strong>: A pessoa que criou/aplicou o commit e a data/hora&lt;/li>
&lt;li>&lt;strong>Mensagem de commit&lt;/strong>: Texto explicando a intenção da alteração&lt;/li>
&lt;/ol>
&lt;p>Matematicamente, o valor do hash $H(C)$ para um objeto de commit $C$ é definido da seguinte forma:&lt;/p>
$$
H(C) = \text{SHA-1}( \text{tree} \parallel \text{parent} \parallel \text{author} \parallel \text{committer} \parallel \text{message} )
$$&lt;p>Aqui, $\parallel$ representa a concatenação de dados. Devido às propriedades da função de hash, mudar até mesmo um único caractere na mensagem de commit, ou ter um commit pai diferente, gerará um valor de hash completamente diferente. Ou seja, &lt;strong>os commits são imutáveis (Immutable)&lt;/strong>. Quando dizemos que o &lt;code>rebase&lt;/code>, que será explicado mais adiante, &amp;ldquo;reescreve o histórico&amp;rdquo;, na verdade ele &amp;ldquo;cria novos commits com conteúdos semelhantes, mas com hashes diferentes&amp;rdquo;.&lt;/p>
&lt;p>O tamanho do espaço de hash é $2^{160}$ e a probabilidade $P$ de ocorrer uma colisão (dois commits diferentes com o mesmo valor de hash) pode ser aproximada usando a teoria do Paradoxo do Aniversário (onde $n$ é o número de commits):&lt;/p>
$$
P(\text{collision}) \approx 1 - \exp\left(-\frac{n^2}{2 \times 2^{160}}\right)
$$&lt;p>Essa probabilidade é extremamente baixa e, na prática, é quase impossível que os hashes de commit do Git colidam.&lt;/p>
&lt;hr>
&lt;h1 id="3-teoria-dos-grafos-e-dag-o-modelo-matemático-do-histórico-do-git">3. Teoria dos grafos e DAG: O modelo matemático do histórico do Git
&lt;/h1>&lt;p>O histórico de commits do Git é modelado como um &amp;ldquo;Grafo Acíclico Direcionado (DAG - Directed Acyclic Graph)&amp;rdquo; na teoria dos grafos.&lt;/p>
&lt;h2 id="31-o-que-é-um-dag-grafo-acíclico-direcionado">3.1 O que é um DAG (Grafo Acíclico Direcionado)
&lt;/h2>&lt;p>Em um grafo $G = (V, E)$, $V$ é o conjunto de commits (vértices) e $E$ é o conjunto de arestas direcionadas que indicam as relações pai-filho entre os commits. No Git, a direção das arestas vai &amp;ldquo;do commit filho para o commit pai&amp;rdquo;. Isso ocorre porque um novo commit contém um ponteiro para um commit passado.&lt;/p>
&lt;pre class="mermaid">
graph BT
A[&amp;#34;Commit A (Inicial)&amp;#34;]
B[&amp;#34;Commit B&amp;#34;]
C[&amp;#34;Commit C (Main)&amp;#34;]
D[&amp;#34;Commit D (Feature)&amp;#34;]
E[&amp;#34;Commit 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>A maior característica do DAG é a &amp;ldquo;ausência de ciclos (loops)&amp;rdquo;. Isso garante que algoritmos que rastreiam o histórico de commits nunca entrem em um loop infinito e alcancem com segurança o fim (o commit inicial).&lt;/p>
&lt;h2 id="32-ordenação-topológica-e-a-ordem-do-histórico">3.2 Ordenação topológica e a ordem do histórico
&lt;/h2>&lt;p>Quando o histórico é exibido usando comandos como &lt;code>git log&lt;/code>, o DAG é ordenado como uma lista unidimensional usando o algoritmo de Ordenação Topológica (Topological Sort). Para qualquer aresta direcionada $u \to v$ no DAG ($u$ é filho de $v$), ele é reorganizado para que $u$ venha antes de $v$ na lista.&lt;/p>
&lt;hr>
&lt;h1 id="4-mecanismos-e-tipos-de-git-merge">4. Mecanismos e tipos de git merge
&lt;/h1>&lt;p>O comando mais básico para integrar alterações de uma branch é o &lt;code>git merge&lt;/code>. No entanto, dependendo do estado atual, o Git escolhe automaticamente estratégias de merge diferentes.&lt;/p>
&lt;h2 id="41-merge-fast-forward---ff">4.1 Merge Fast-Forward (&amp;ndash;ff)
&lt;/h2>&lt;p>Se a branch de destino (ex: &lt;code>main&lt;/code>) for um ancestral direto da branch de origem (ex: &lt;code>feature&lt;/code>), o Git executará um merge &amp;ldquo;Fast-Forward&amp;rdquo; (avanço rápido). Esta operação não cria um novo commit; ela simplesmente avança o ponteiro da branch.&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>Um merge Fast-Forward mantém o histórico em linha reta, mas tem a desvantagem de perder o contexto de &amp;ldquo;quais commits agrupavam o desenvolvimento de uma única funcionalidade (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>Ao especificar explicitamente &lt;code>git merge --no-ff&lt;/code>, o Git sempre criará um novo &amp;ldquo;commit de merge&amp;rdquo;, mesmo que um Fast-Forward seja possível. Um commit de merge é um commit especial que tem dois pais.&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;Trabalho Principal 1&amp;#34;
merge feature type: NORMAL
&lt;/pre>
&lt;p>A vantagem desse método é que a existência e a história da branch da funcionalidade permanecem claras no DAG. Se ocorrer um problema, é possível desfazer (reverter) com segurança a funcionalidade inteira de uma vez, executando &lt;code>git revert -m 1 &amp;lt;hash do commit de merge&amp;gt;&lt;/code>.&lt;/p>
&lt;h2 id="43-algoritmo-3-way-merge-merge-de-3-vias">4.3 Algoritmo 3-Way Merge (Merge de 3 vias)
&lt;/h2>&lt;p>Se a branch de destino e a branch de origem tiverem seus próprios commits independentes, o Git executará um merge de 3 vias. Neste caso, o Git explora o DAG e encontra o &amp;ldquo;ancestral comum mais baixo&amp;rdquo; (LCA - Lowest Common Ancestor) das duas branches.&lt;/p>
&lt;p>A complexidade computacional $T_{\text{LCA}}$ do algoritmo para encontrar o LCA pode ser executada em tempo linear em relação ao número de vértices $|V|$ e arestas $|E|$:&lt;/p>
$$
T_{\text{LCA}} = \mathcal{O}(|V| + |E|)
$$&lt;p>O Git compara três estados: o &amp;ldquo;estado do LCA&amp;rdquo;, o &amp;ldquo;estado da branch atual&amp;rdquo; e o &amp;ldquo;estado da outra branch&amp;rdquo;. Se as alterações não entrarem em conflito, ele gerará um commit de merge automaticamente.&lt;/p>
&lt;hr>
&lt;h1 id="5-mecanismo-do-git-rebase-e-reconstrução-do-histórico">5. Mecanismo do git rebase e reconstrução do histórico
&lt;/h1>&lt;p>Enquanto o &lt;code>git merge&lt;/code> &amp;ldquo;integra&amp;rdquo; o histórico, o &lt;code>git rebase&lt;/code> &amp;ldquo;reconstrói (reorganiza)&amp;rdquo; o histórico.&lt;/p>
&lt;h2 id="51-o-processo-por-trás-do-rebase">5.1 O processo por trás do Rebase
&lt;/h2>&lt;p>Quando você faz o rebase da branch &lt;code>feature&lt;/code> na branch &lt;code>main&lt;/code> (&lt;code>git rebase main&lt;/code>), as operações internas são as seguintes:&lt;/p>
&lt;ol>
&lt;li>Encontra o ancestral comum (LCA) entre a branch &lt;code>feature&lt;/code> e a branch &lt;code>main&lt;/code>.&lt;/li>
&lt;li>Salva as diferenças (diffs) dos commits, do LCA até o topo da branch &lt;code>feature&lt;/code>, em uma área temporária.&lt;/li>
&lt;li>Move o ponteiro da branch &lt;code>feature&lt;/code> para o topo da branch &lt;code>main&lt;/code>.&lt;/li>
&lt;li>Aplica sequencialmente (Cherry-Pick) as diferenças salvas na nova base (o topo da &lt;code>main&lt;/code>), criando novos commits um por um.&lt;/li>
&lt;/ol>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Commit A&amp;#34;] --&amp;gt; B[&amp;#34;Commit B&amp;#34;]
B --&amp;gt; C[&amp;#34;Commit C (Main)&amp;#34;]
B --&amp;gt; D[&amp;#34;Commit D (Feature Antigo)&amp;#34;]
D -.-&amp;gt; E[&amp;#34;Commit D&amp;#39; (Novo Feature)&amp;#34;]
C --&amp;gt; E
style D stroke-dasharray: 5 5, fill: #f9f9f9, color: #999
&lt;/pre>
&lt;p>O ponto crucial aqui é que o commit gerado pelo rebase, $D'$, tem um &lt;strong>commit pai diferente do commit original $D$, portanto, ele tem um valor de hash completamente diferente&lt;/strong> (consulte a definição da função de hash $H(C)$ mencionada anteriormente).&lt;/p>
&lt;h2 id="52-interactive-rebase-rebase-interativo">5.2 Interactive Rebase (Rebase Interativo)
&lt;/h2>&lt;p>Ao usar &lt;code>git rebase -i&lt;/code> (ou &lt;code>--interactive&lt;/code>), você pode manipular o histórico de commits como quiser. Esta é a ferramenta mais poderosa para organizar seu histórico local.&lt;/p>
&lt;ul>
&lt;li>&lt;code>pick&lt;/code>: Mantém o commit como está.&lt;/li>
&lt;li>&lt;code>reword&lt;/code>: Altera apenas a mensagem do commit.&lt;/li>
&lt;li>&lt;code>edit&lt;/code>: Pausa para modificar o conteúdo do commit.&lt;/li>
&lt;li>&lt;code>squash&lt;/code>: Funde este commit com o commit anterior, combinando também suas mensagens.&lt;/li>
&lt;li>&lt;code>fixup&lt;/code>: Semelhante ao &lt;code>squash&lt;/code>, mas descarta a mensagem deste commit.&lt;/li>
&lt;li>&lt;code>drop&lt;/code>: Remove o commit completamente.&lt;/li>
&lt;/ul>
&lt;p>Matematicamente, se uma branch tem $N$ commits, as variações de histórico linear (permutações) $P$ que podem ser geradas pela reordenação no rebase são:&lt;/p>
$$
P = N!
$$&lt;p>O Git dá aos desenvolvedores $N!$ opções, permitindo-lhes manter o histórico lógico e elegante.&lt;/p>
&lt;hr>
&lt;h1 id="6-a-regra-de-ouro-do-rebase-the-golden-rule-of-rebase">6. A Regra de Ouro do Rebase (The Golden Rule of Rebase)
&lt;/h1>&lt;p>Embora o &lt;code>rebase&lt;/code> seja muito poderoso, existe uma regra absoluta:&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>&amp;ldquo;Nunca faça rebase em um histórico público compartilhado.&amp;rdquo;&lt;/strong>
&lt;em>(Never rebase public history)&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;h2 id="61-por-que-você-não-deve-fazer-rebase-em-um-histórico-público">6.1 Por que você não deve fazer rebase em um histórico público?
&lt;/h2>&lt;p>O Git é distribuído. Os commits que você enviou para o &lt;code>origin/main&lt;/code> também foram clonados nos repositórios locais de outros desenvolvedores. Se você fizer rebase de commits já enviados, reescrever o histórico e sobrescrever à força com &lt;code>git push --force&lt;/code>, o que acontecerá?&lt;/p>
&lt;p>O DAG no repositório local de outros desenvolvedores divergiria fundamentalmente do DAG remoto. Quando outros desenvolvedores executarem &lt;code>git pull&lt;/code>, o Git tentará forçar o merge de grupos de commits com históricos diferentes, gerando um número massivo de conflitos e commits duplicados (mesmo conteúdo, mas hashes diferentes), deixando o repositório em pânico.&lt;/p>
&lt;p>O rebase só deve ser executado em &lt;strong>&amp;ldquo;branches locais que você ainda não compartilhou com ninguém&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;hr>
&lt;h1 id="7-resolução-de-conflitos-e-git-rebase---continue">7. Resolução de conflitos e git rebase &amp;ndash;continue
&lt;/h1>&lt;p>Se várias pessoas alterarem a mesma parte do mesmo arquivo, ocorrerá um conflito. O processo de resolução de conflitos é diferente entre &lt;code>merge&lt;/code> e &lt;code>rebase&lt;/code>.&lt;/p>
&lt;h2 id="71-resolução-de-conflitos-no-merge">7.1 Resolução de conflitos no Merge
&lt;/h2>&lt;p>Com o &lt;code>git merge&lt;/code>, a resolução de conflitos ocorre &lt;strong>apenas uma vez&lt;/strong>. Antes de criar o commit de merge final, você corrige todas as partes conflitantes de uma só vez.&lt;/p>
&lt;h2 id="72-resolução-de-conflitos-no-rebase">7.2 Resolução de conflitos no Rebase
&lt;/h2>&lt;p>Com o &lt;code>git rebase&lt;/code>, como os commits são reaplicados um por um, &lt;strong>podem ocorrer conflitos em cada commit&lt;/strong>.&lt;/p>
&lt;p>Se ocorrer um conflito durante um rebase, o Git irá pausar o processo. O fluxo de resolução é o seguinte:&lt;/p>
&lt;ol>
&lt;li>Abra seu editor ou IDE (como VS Code) e resolva manualmente os marcadores de conflito (&lt;code>&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/code>, &lt;code>======&lt;/code> e &lt;code>&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code>).&lt;/li>
&lt;li>Adicione os arquivos modificados ao índice:
&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;arquivo_modificado&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;/li>
&lt;li>Retome o processo de rebase sem criar um novo commit:
&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>Se você quiser cancelar o rebase e retornar ao estado original, execute o seguinte comando:&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>Se a resolução do conflito não for necessária e você quiser pular aquele commit específico, use &lt;code>git rebase --skip&lt;/code>&lt;/em>)&lt;/p>
&lt;hr>
&lt;h1 id="8-uso-correto-na-prática-workflow-prático">8. Uso correto na prática (Workflow prático)
&lt;/h1>&lt;p>Então, como devemos escolher entre &lt;code>merge&lt;/code> e &lt;code>rebase&lt;/code> no desenvolvimento real? Aqui apresentamos a abordagem mais padrão e segura.&lt;/p>
&lt;h2 id="81-cenário-1-organizar-o-histórico-de-trabalho-local-uso-do-rebase">8.1 [Cenário 1] Organizar o histórico de trabalho local (Uso do Rebase)
&lt;/h2>&lt;p>Imagine que, ao desenvolver em uma branch de funcionalidade, muitos commits pequenos (&amp;ldquo;correção de erro de digitação&amp;rdquo;, &amp;ldquo;salvamento temporário&amp;rdquo;, etc.) se acumularam. Antes de abrir um Pull Request (PR), você usa o rebase interativo para organizá-los em unidades significativas.&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"># Execute enquanto estiver na branch 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"># (O editor será aberto. Use squash ou fixup para limpar o histórico)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Isso criará um histórico de commits limpo e fácil para os revisores entenderem sua intenção.&lt;/p>
&lt;h2 id="82-cenário-2-acompanhar-as-atualizações-da-branch-main-uso-do-rebase">8.2 [Cenário 2] Acompanhar as atualizações da branch main (Uso do Rebase)
&lt;/h2>&lt;p>Se o desenvolvimento demorar e alterações de outras pessoas continuarem sendo mescladas na branch &lt;code>main&lt;/code>, sua branch &lt;code>feature&lt;/code> ficará desatualizada. Neste caso, faça o rebase da sua branch &lt;code>feature&lt;/code> na branch &lt;code>main&lt;/code> mais recente.&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"># Obtenha as informações mais recentes da 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"># Faça o rebase da branch feature na versão mais recente da main&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>Isso mantém o histórico linear e evita conflitos em merges futuros. Também evita a criação de commits de merge desnecessários (&amp;ldquo;Merge branch &amp;lsquo;main&amp;rsquo; into feature&amp;rdquo;).&lt;/p>
&lt;h2 id="83-cenário-3-integrar-uma-funcionalidade-concluída-uso-do-merge">8.3 [Cenário 3] Integrar uma funcionalidade concluída (Uso do Merge)
&lt;/h2>&lt;p>O desenvolvimento na branch &lt;code>feature&lt;/code> está concluído e é hora de integrá-lo à branch &lt;code>main&lt;/code>. Aqui, usamos &lt;strong>&lt;code>git merge --no-ff&lt;/code>&lt;/strong> (equivalente a escolher &amp;ldquo;Create a merge commit&amp;rdquo; em um Pull Request do GitHub).&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: Implementação da funcionalidade de login de usuário&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>Isso deixa um ponto de conexão histórico (commit de merge) no DAG da branch &lt;code>main&lt;/code> dizendo que &amp;ldquo;uma funcionalidade foi mesclada aqui&amp;rdquo;. Ao olhar para o histórico mais tarde, será mais fácil rastrear o código por funcionalidade.&lt;/p>
&lt;hr>
&lt;h1 id="9-conclusão">9. Conclusão
&lt;/h1>&lt;p>Na operação do Git, as abordagens extremas de &amp;ldquo;fazer tudo com Merge&amp;rdquo; ou &amp;ldquo;manter tudo linear com Rebase&amp;rdquo; têm seus prós e contras.&lt;/p>
&lt;p>A melhor prática no mundo real é uma abordagem híbrida: &lt;strong>&amp;ldquo;organize o histórico privado local de forma elegante com rebase e deixe o contexto no histórico público de integração com merge &amp;ndash;no-ff&amp;rdquo;&lt;/strong>.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Local (Espaço de trabalho pessoal)&lt;/strong>: Use o &lt;code>rebase&lt;/code> para eliminar commits desnecessários e acompanhar a linha principal mais recente para manter um histórico linear.&lt;/li>
&lt;li>&lt;strong>Global (Espaço de trabalho compartilhado)&lt;/strong>: Use &lt;code>merge --no-ff&lt;/code> para registrar a existência da branch de funcionalidade como um commit de merge no DAG, facilitando a reversão e o rastreamento.&lt;/li>
&lt;/ul>
&lt;p>Ao compreender a matemática e a arquitetura por trás da estrutura DAG e da função hash, os comandos Git passam da simples memorização para um &amp;ldquo;design de histórico intencional&amp;rdquo;. Seguindo a Regra de Ouro do Rebase, vamos selecionar os comandos adequados à situação e construir um histórico de commits limpo, legível e de fácil manutenção para toda a equipe.&lt;/p></description></item><item><title>Construindo um Pipeline CI/CD para Projetos C++ usando GitHub Actions</title><link>http://kenji.blog/pt/p/github-actions-cpp-cicd-pipeline/</link><pubDate>Sat, 12 Sep 2026 18:00:00 +0900</pubDate><guid>http://kenji.blog/pt/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 Construindo um Pipeline CI/CD para Projetos C++ usando GitHub Actions" />&lt;h1 id="construindo-um-pipeline-cicd-para-projetos-c-usando-github-actions-o-guia-completo">Construindo um Pipeline CI/CD para Projetos C++ usando GitHub Actions: O Guia Completo
&lt;/h1>&lt;p>No paradigma moderno de desenvolvimento de software, a Integração Contínua (Continuous Integration: CI) e a Entrega/Implantação Contínua (Continuous Delivery/Deployment: CD) são elementos indispensáveis para processos de desenvolvimento ágeis e para a manutenção de software de alta qualidade. Com inúmeras linguagens de programação disponíveis, a construção de um pipeline CI/CD em C++ envolve dificuldades e complexidades únicas em comparação com outras linguagens (como Python, JavaScript, Go, etc.).&lt;/p>
&lt;p>Neste artigo, explicaremos de forma extremamente detalhada como construir um pipeline CI/CD robusto e prático do zero para um projeto C++ utilizando o GitHub Actions. Cobriremos todas as técnicas práticas, incluindo builds em matriz multiplataforma (Windows, Linux, macOS), integração do sistema de build com CMake, testes automatizados usando CTest, automação de análises estáticas e dinâmicas, medição de cobertura (coverage) e entrega automática de binários compilados através do GitHub Releases.&lt;/p>
&lt;h2 id="1-o-significado-e-os-desafios-únicos-do-cicd-em-projetos-c">1. O Significado e os Desafios Únicos do CI/CD em Projetos C++
&lt;/h2>&lt;p>No desenvolvimento de aplicações web ou uso de linguagens de script, testar e compilar em um único contêiner Docker costuma ser suficiente na maioria dos casos. No entanto, o C++ é uma linguagem compilada nativamente e depende fortemente da arquitetura de hardware e do sistema operacional do ambiente de execução.&lt;/p>
&lt;p>Ao introduzir CI/CD em um projeto C++, os principais desafios enfrentados são:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Diversidade de Plataformas&lt;/strong>: Diferentes SOs como Windows, Linux e macOS têm APIs distintas (Windows API, POSIX, etc.). É muito comum que um código funcione no ambiente local do desenvolvedor (por exemplo, macOS) mas falhe na compilação no Linux ou Windows.&lt;/li>
&lt;li>&lt;strong>Diferenças entre Compiladores&lt;/strong>: Compiladores principais como Microsoft Visual C++ (MSVC), GNU Compiler Collection (GCC) e Clang variam no grau de implementação do padrão C++ (C++17, C++20, C++23), em suas interpretações e na severidade dos avisos (warnings).&lt;/li>
&lt;li>&lt;strong>Tempo de Compilação (Build)&lt;/strong>: Em projetos C++ de grande escala, não é incomum que a compilação demore de dezenas de minutos a várias horas. No ambiente de CI, com recursos computacionais limitados, estratégias de cache e paralelização são necessárias para compilar de forma eficiente.&lt;/li>
&lt;li>&lt;strong>Gerenciamento de Dependências&lt;/strong>: Diferente de npm ou pip, o C++ não possui um gerenciador de pacotes padrão absoluto. É necessário resolver bibliotecas corretamente em cada execução no ambiente CI usando ferramentas como vcpkg, Conan ou o &lt;code>FetchContent&lt;/code> do CMake.&lt;/li>
&lt;li>&lt;strong>Gerenciamento de Memória e Comportamento Indefinido (Undefined Behavior)&lt;/strong>: Como envolve manipulação de ponteiros e gerenciamento manual de memória, além de testar a lógica, também é necessário automatizar a detecção de vazamentos de memória (memory leaks) e comportamentos indefinidos.&lt;/li>
&lt;/ol>
&lt;p>Para resolver esses desafios, o GitHub Actions se torna a solução ideal, pois permite provisionar várias máquinas virtuais de SO sob demanda e definir fluxos de trabalho complexos via código (Configuration as Code).&lt;/p>
&lt;h2 id="2-visão-geral-da-arquitetura-do-pipeline-cicd">2. Visão Geral da Arquitetura do Pipeline CI/CD
&lt;/h2>&lt;p>Vamos visualizar todo o pipeline CI/CD que construiremos. O diagrama de sequência Mermaid a seguir mostra o fluxo de trabalho desde o push do código até a release.&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant Dev as &amp;#34;Desenvolvedor&amp;#34;
participant Repo as &amp;#34;Repositório 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;Fazer push da branch / Abrir PR&amp;#34;
Repo-&amp;gt;&amp;gt;Action: &amp;#34;Acionar Workflow de CI&amp;#34;
activate Action
Action-&amp;gt;&amp;gt;Action: &amp;#34;Lint &amp;amp; Análise Estática (Clang-Tidy)&amp;#34;
rect rgb(200, 220, 240)
note right of Action: &amp;#34;Build em Matriz Multiplataforma&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;Build no Ubuntu (GCC/Clang)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;Build no Windows (MSVC)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;Build no macOS (Apple Clang)&amp;#34;
end
Action-&amp;gt;&amp;gt;Action: &amp;#34;Executar CTest (com ASAN/UBSAN)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;Gerar Relatório de Cobertura&amp;#34;
alt &amp;#34;Se uma Tag sofrer push (ex: v1.0.0)&amp;#34;
Action-&amp;gt;&amp;gt;Action: &amp;#34;Empacotar Binários com CPack&amp;#34;
Action-&amp;gt;&amp;gt;Rel: &amp;#34;Fazer upload do ZIP/Tarball para a Release&amp;#34;
end
deactivate Action
Repo--&amp;gt;&amp;gt;Dev: &amp;#34;Reportar Status do CI (Passou/Falhou)&amp;#34;
&lt;/pre>
&lt;p>Nessa arquitetura, fornecemos feedback rápido (análise estática, build e testes) na fase de Pull Request, e realizamos o empacotamento e a distribuição dos artefatos no momento em que uma tag de versão é adicionada.&lt;/p>
&lt;h2 id="3-configuração-do-projeto-com-cmake-moderno">3. Configuração do Projeto com CMake Moderno
&lt;/h2>&lt;p>A base de um excelente pipeline de CI é um sistema de build robusto. Usaremos o CMake, que é o padrão de fato para C++. Aqui, adotaremos a abordagem orientada a alvos (target-oriented) conhecida como &amp;ldquo;CMake Moderno&amp;rdquo;.&lt;/p>
&lt;p>Assumimos a seguinte estrutura de diretórios para o projeto:&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>Exemplo de configuração do &lt;code>CMakeLists.txt&lt;/code> na raiz:&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"># Configuração do padrão 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"># Desativa extensões específicas do compilador para aumentar a portabilidade
&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"># Rigor nos avisos do compilador
&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"># Criação do alvo da biblioteca
&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"># Criação do alvo do executável
&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"># Habilitação de testes
&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"># Definição das regras de instalação (para 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"># Configurações de empacotamento com 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>Pontos importantes:&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>&lt;code>CMAKE_CXX_EXTENSIONS OFF&lt;/code>: Previne a dependência de recursos não padronizados, como extensões GNU, garantindo a natureza multiplataforma.&lt;/li>
&lt;li>&lt;strong>Rigor nos avisos (&lt;code>-Werror&lt;/code> / &lt;code>/WX&lt;/code>)&lt;/strong>: Ao tratar os avisos do compilador como erros no ambiente de CI, mantemos a qualidade do código forçadamente alta.&lt;/li>
&lt;li>&lt;strong>GNUInstallDirs&lt;/strong>: Resolve automaticamente os caminhos de instalação padrão específicos do SO (como &lt;code>/usr/local/bin&lt;/code> ou &lt;code>C:\Program Files&lt;/code>).&lt;/li>
&lt;/ul>
&lt;h2 id="4-o-básico-do-github-actions-e-a-estratégia-de-matriz">4. O Básico do GitHub Actions e a Estratégia de Matriz
&lt;/h2>&lt;p>O GitHub Actions é configurado através de arquivos YAML dentro do diretório &lt;code>.github/workflows/&lt;/code>.
O recurso mais poderoso para projetos C++ é a &amp;ldquo;Estratégia de Matriz&amp;rdquo; (Matrix Strategy). Isso permite gerar dinamicamente combinações de SOs e compiladores e executá-las em paralelo.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Acionar Workflow&amp;#34;] --&amp;gt; B[&amp;#34;Avaliação de Job em Matriz&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>Abaixo mostramos a definição de job em YAML, que é a base para o build em matriz.&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"># Continua o build de outros SOs mesmo se um job falhar&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> é muito importante. Por exemplo, se usarmos por engano uma API exclusiva do Linux, o build no Ubuntu falhará, mas ainda queremos ver ao mesmo tempo se o build no Windows é bem-sucedido.&lt;/p>
&lt;h2 id="5-otimização-do-tempo-de-build-e-da-limitação-de-custo-com-a-lei-de-amdahl">5. Otimização do Tempo de Build e da Limitação de Custo com a Lei de Amdahl
&lt;/h2>&lt;p>CI/CD em ambiente de nuvem é uma batalha contra o tempo, e o tempo de build se traduz diretamente no tempo de espera do desenvolvedor e nos custos de execução.
Aqui, abordaremos matematicamente a otimização do tempo de compilação usando a &amp;ldquo;Lei de Amdahl&amp;rdquo; (Amdahl&amp;rsquo;s Law) da ciência da computação.&lt;/p>
&lt;p>A Lei de Amdahl define que, quando a proporção da parte paralelizada de um programa é $P$, o ganho máximo teórico de velocidade $S(N)$ ao usar $N$ processadores é:&lt;/p>
$$ S(N) = \frac{1}{(1 - P) + \frac{P}{N}} $$&lt;p>No processo de build do C++, a compilação de cada unidade de tradução (Translation Unit: arquivos &lt;code>.cpp&lt;/code>) do código-fonte é totalmente independente e pode ser paralelizada. Por outro lado, a configuração do CMake e a fase de vinculação (linkagem) do binário final geralmente são executadas em série (não podem ser paralelizadas).&lt;/p>
&lt;p>Suponha que, de todo o tempo de build do projeto, 80% seja a fase de compilação ($P = 0.8$) e 20% seja a fase serial ($1 - P = 0.2$).
Os runners padrão do GitHub Actions (Linux) fornecem 2 núcleos (threads). Portanto, para $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>Usando apenas 2 núcleos, obtemos uma melhoria de velocidade de cerca de 1,67 vezes. Para alcançar isso, é essencial especificar a opção &lt;code>--parallel&lt;/code> no comando de build do 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;/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>Além disso, também consideramos os custos. O custo de utilização do GitHub Actions $C_{total}$ é a soma dos produtos do tempo de execução do job $T_i$ e do preço unitário do runner $R_i$.&lt;/p>
$$ C_{total} = \sum_{i=1}^{M} \left( T_i \times R_i \right) $$&lt;p>Reduzir o tempo de build não apenas acelera o ciclo de feedback, mas também leva diretamente à redução dos custos operacionais do projeto (especialmente para repositórios privados). Se precisar de mais velocidade, é válido adotar o &lt;code>ccache&lt;/code> para fazer cache dos resultados de compilação.&lt;/p>
&lt;h2 id="6-testes-automatizados-e-integração-de-sanitizers">6. Testes Automatizados e Integração de Sanitizers
&lt;/h2>&lt;p>Para evitar bugs precocemente em C++, é altamente recomendado introduzir &amp;ldquo;sanitizers&amp;rdquo;, além de testes de unidade, para detectar vazamentos de memória e comportamentos indefinidos durante a execução. Usaremos o AddressSanitizer (ASAN) e o UndefinedBehaviorSanitizer (UBSAN) desenvolvidos pelo Google.&lt;/p>
&lt;p>Adicionamos uma opção no CMake para habilitar os sanitizers.&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>Habilitamos essa opção no job do Ubuntu do pipeline de CI e executamos os testes.&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>O comando &lt;code>ctest&lt;/code> é usado para executar os testes. Especificando &lt;code>--output-on-failure&lt;/code>, apenas os logs detalhados de testes reprovados serão exibidos na saída do CI, evitando que o log se torne enorme.&lt;/p>
&lt;h2 id="7-medição-da-cobertura-de-código-coverage">7. Medição da Cobertura de Código (Coverage)
&lt;/h2>&lt;p>Visualizar quanto de código os testes estão cobrindo é importante para a garantia de qualidade. Usando o ambiente Linux (GCC), medimos a cobertura usando &lt;code>gcov&lt;/code> e &lt;code>lcov&lt;/code>.&lt;/p>
&lt;p>Primeiro, configuramos as flags de compilação no CMake para medir a cobertura.&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>Definimos um job separado no GitHub Actions para medir a cobertura.&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>O comando &lt;code>lcov --remove&lt;/code> é usado para excluir headers de sistema, bibliotecas de terceiros e o próprio código de teste da medição de cobertura. Isso garante que a cobertura seja unicamente do código-fonte específico do projeto.&lt;/p>
&lt;h2 id="8-entrega-automática-de-binários-através-do-github-releases-cd">8. Entrega Automática de Binários através do GitHub Releases (CD)
&lt;/h2>&lt;p>Construímos a parte &amp;ldquo;CD&amp;rdquo; do CI/CD. Quando os desenvolvedores adicionam e fazem push de uma tag de versão com Git (ex: &lt;code>v1.2.0&lt;/code>), os binários executáveis ​​para cada SO são compilados, empacotados em arquivos ZIP ou Tarball e enviados para o GitHub Releases.&lt;/p>
&lt;p>Nesta etapa, usaremos o &lt;code>CPack&lt;/code>, a ferramenta de empacotamento incluída no 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>Com essa configuração, ao executar &lt;code>git tag v1.0.0&lt;/code> e &lt;code>git push origin v1.0.0&lt;/code>, um arquivo ZIP para usuários do Windows e um Tarball para usuários Linux/macOS serão automaticamente disponibilizados na página de lançamento, sem intervenção manual. Esse recurso é extremamente poderoso na entrega de software aos usuários.&lt;/p>
&lt;h2 id="9-arquivo-workflow-yaml-completo">9. Arquivo Workflow YAML Completo
&lt;/h2>&lt;p>Abaixo apresentamos o código completo de um arquivo &lt;code>.github/workflows/main.yml&lt;/code> robusto e prático, integrando todos os elementos discutidos até agora.&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-para-um-cicd-ainda-mais-avançado-análise-estática-e-formatação">10. Para um CI/CD ainda mais Avançado (Análise Estática e Formatação)
&lt;/h2>&lt;p>Embora omitiremos os detalhes aqui, na prática é recomendado incorporar mais ferramentas de garantia de qualidade ao pipeline.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Obrigatoriedade do Clang-Format&lt;/strong>: Para reduzir a carga de trabalho na revisão de código, integre verificações de estilo de código com &lt;code>clang-format&lt;/code> no CI, falhando o pipeline em caso de violação das regras de formatação.&lt;/li>
&lt;li>&lt;strong>Análise Estática (Clang-Tidy)&lt;/strong>: Para detectar bugs em potencial que avisos de compilador não capturam, além de códigos ineficientes (como cópias desnecessárias), integre o &lt;code>clang-tidy&lt;/code> ao CMake e execute-o no CI.&lt;/li>
&lt;li>&lt;strong>Uso de Cache do vcpkg / Conan&lt;/strong>: Se o projeto usa várias bibliotecas de terceiros, compilar as dependências exigirá um tempo considerável. Aproveitando &lt;code>actions/cache&lt;/code> do GitHub Actions, podemos manter os diretórios instalados do vcpkg ou o cache do Conan, o que reduz drasticamente o tempo de compilação.&lt;/li>
&lt;/ol>
&lt;h2 id="conclusão">Conclusão
&lt;/h2>&lt;p>Construir um pipeline CI/CD em um projeto C++ pode, à primeira vista, parecer uma barreira difícil devido às dependências de plataforma e à complexidade das ferramentas de build. No entanto, combinando adequadamente o ecossistema do GitHub Actions, CMake moderno e CTest/CPack, você pode obter um fluxo de desenvolvimento incrivelmente poderoso e automatizado.&lt;/p>
&lt;p>A verificação multiplataforma por meio da estratégia de matriz, a detecção de erros de execução com sanitizers, a medição de cobertura (coverage) e a implantação automática via GitHub Releases explicados neste artigo são as melhores práticas amplamente adotadas mesmo em projetos open-source de nível comercial.&lt;/p>
&lt;p>Os pipelines de CI/CD automatizados minimizam o tempo que os desenvolvedores gastam para &amp;ldquo;procurar bugs&amp;rdquo; ou executar trabalhos de &amp;ldquo;build e releases manuais&amp;rdquo;, tornando-se a arma definitiva para concentrar o foco no que realmente importa: na criação e na programação. Por favor, traga esses conceitos e os implemente no seu projeto C++, de forma a garantir uma vida de desenvolvimento mais ágil e despreocupada!&lt;/p></description></item><item><title>Como hospedar um blog Hugo grátis e super rápido no Cloudflare Pages ou GitHub Pages</title><link>http://kenji.blog/pt/p/hugo-hosting-cloudflare-vs-github-pages/</link><pubDate>Sat, 12 Sep 2026 12:00:00 +0900</pubDate><guid>http://kenji.blog/pt/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 Como hospedar um blog Hugo grátis e super rápido no Cloudflare Pages ou GitHub Pages" />&lt;p>Ao gerenciar um site ou blog, a velocidade de carregamento (desempenho), os custos operacionais e a segurança são fatores extremamente importantes. No passado, a combinação de um CMS dinâmico (Content Management System) como o WordPress e um servidor de hospedagem era predominante, mas hoje a arquitetura chamada &amp;ldquo;Jamstack&amp;rdquo; está atraindo muita atenção. Entre as opções, combinando o &amp;ldquo;Hugo&amp;rdquo; - um gerador de sites estáticos (SSG) ultrarrápido escrito em Go - com serviços de hospedagem modernos como o Cloudflare Pages e o GitHub Pages, é possível construir um ambiente de blog &lt;strong>totalmente gratuito e incrivelmente rápido&lt;/strong>.&lt;/p>
&lt;p>Neste artigo, explicaremos a fundo, sob uma perspectiva técnica, as etapas específicas para publicar um site estático usando Hugo no Cloudflare Pages ou GitHub Pages, as diferenças na arquitetura de cada plataforma, a construção de CI/CD (Integração Contínua/Implantação Contínua) usando o GitHub Actions, a otimização de DNS, estratégias de cache e até a introdução de análises de acesso que respeitam a privacidade.&lt;/p>
&lt;hr>
&lt;h2 id="1-o-básico-sobre-geradores-de-sites-estáticos-ssg-e-jamstack">1. O Básico sobre Geradores de Sites Estáticos (SSG) e Jamstack
&lt;/h2>&lt;h3 id="11-por-que-sites-estáticos">1.1 Por que sites estáticos?
&lt;/h3>&lt;p>Sistemas de CMS dinâmicos tradicionais (ex: WordPress) enviam consultas a um banco de dados (como MySQL) e geram o HTML dinamicamente no lado do servidor (como PHP) para cada solicitação do usuário. Embora esse método seja altamente flexível, ele possui baixa tolerância a picos repentinos de tráfego (ou seja, quando algo viraliza ou ataques DDoS) e frequentemente exige uma arquitetura de infraestrutura complexa, como colocar servidores de cache (Redis ou Varnish) na frente.&lt;/p>
&lt;p>Por outro lado, geradores de sites estáticos (SSG) que adotam a arquitetura Jamstack (JavaScript, APIs e Markup) geram antecipadamente (no momento do build) todos os arquivos HTML, CSS e JavaScript. Para as solicitações do usuário, o servidor web (ou CDN) simplesmente retorna os arquivos estáticos já gerados, o que permite alcançar uma velocidade impressionante e segurança robusta.&lt;/p>
&lt;h3 id="12-a-vantagem-do-hugo">1.2 A Vantagem do Hugo
&lt;/h3>&lt;p>Há várias opções de SSG, como Next.js, Gatsby, Jekyll e Astro, mas a maior característica do Hugo é a sua &lt;strong>velocidade de compilação&lt;/strong>. Beneficiando-se do processamento simultâneo fornecido pela linguagem Go, a compilação é concluída em apenas alguns segundos, mesmo para sites com milhares a dezenas de milhares de páginas. Isso reduz drasticamente o tempo de espera no pipeline de CI/CD e está diretamente ligado à melhoria da experiência do desenvolvedor (DX: Developer Experience).&lt;/p>
&lt;hr>
&lt;h2 id="2-comparação-da-arquitetura-dos-serviços-de-hospedagem">2. Comparação da Arquitetura dos Serviços de Hospedagem
&lt;/h2>&lt;p>A próxima questão é onde hospedar os arquivos estáticos gerados pelo Hugo. Opções típicas incluem o Cloudflare Pages, o GitHub Pages e o Netlify, mas a arquitetura de rede por trás de cada um deles difere.&lt;/p>
&lt;h3 id="21-cdn-e-computação-de-borda-edge-computing">2.1 CDN e Computação de Borda (Edge Computing)
&lt;/h3>&lt;p>Todas essas plataformas utilizam uma CDN (Content Delivery Network) distribuída globalmente para entregar conteúdo. No entanto, além do simples armazenamento em cache de arquivos estáticos, o fator de diferenciação é se elas podem executar roteamento de solicitações e reescrita de cabeçalhos no PoP (Point of Presence) mais próximo do usuário, usando a &amp;ldquo;computação de borda&amp;rdquo;.&lt;/p>
&lt;pre class="mermaid">
graph TD
A[&amp;#34;Usuário (Tóquio)&amp;#34;] --&amp;gt;|Baixa Latência| B[&amp;#34;Nó de Borda (NRT)&amp;#34;]
C[&amp;#34;Usuário (Nova York)&amp;#34;] --&amp;gt;|Baixa Latência| D[&amp;#34;Nó de Borda (EWR)&amp;#34;]
E[&amp;#34;Usuário (Londres)&amp;#34;] --&amp;gt;|Baixa Latência| F[&amp;#34;Nó de Borda (LHR)&amp;#34;]
B --&amp;gt; G[&amp;#34;Armazenamento de Origem / 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>O GitHub Pages é um serviço que permite publicar arquivos HTML, CSS e JavaScript diretamente de um repositório GitHub. Por trás dele, são utilizadas CDNs como o Fastly, entregando um desempenho adequado. Contudo, possui recursos de infraestrutura pura mais limitados, havendo restrições quanto à personalização de cabeçalhos (ex: configuração de &lt;code>Cache-Control&lt;/code> ou cabeçalhos de segurança), além de que as configurações de redirecionamento dependem de atualizações meta do HTML ou de plugins do Jekyll.&lt;/p>
&lt;h3 id="23-cloudflare-pages">2.3 Cloudflare Pages
&lt;/h3>&lt;p>O Cloudflare Pages é um serviço de hospedagem de sites estáticos construído na maior rede Anycast do mundo (presente em mais de 275 cidades), orgulhosamente fornecida pela Cloudflare.
Ele permite ajustes de desempenho esmagadores, como o suporte padrão ao HTTP/3 (QUIC), otimização de imagens e a integração de funções de borda (Cloudflare Workers). Além disso, não há cobrança por largura de banda, o que significa que pode ser operado gratuitamente independentemente de quão repentinamente o tráfego aumentar, sendo esta uma grande vantagem.&lt;/p>
&lt;h3 id="24-netlify">2.4 Netlify
&lt;/h3>&lt;p>O Netlify é um pioneiro no modelo Jamstack, fornecendo uma DX tudo-em-um que integra funcionalidades de formulário, autenticação (Identity), funções serverless (sem servidor), entre outras. Porém, caso a largura de banda ultrapasse a cota gratuita (100 GB por mês), são geradas cobranças baseadas no consumo que podem ser altas, de modo que é necessário ter cuidado com o controle de custos em blogs que utilizam muitas imagens e vídeos.&lt;/p>
&lt;hr>
&lt;h2 id="3-cálculo-teórico-de-desempenho-e-latência-modelo-matemático-com-latex">3. Cálculo Teórico de Desempenho e Latência (Modelo Matemático com LaTeX)
&lt;/h2>&lt;p>Ao avaliar o desempenho web, a redução da latência (Latency) é o indicador mais importante. Vamos modelar o quanto a latência pode ser reduzida utilizando uma CDN (Borda) em comparação ao acesso direto ao servidor de origem.&lt;/p>
&lt;p>Definiremos a probabilidade da requisição do usuário atingir o cache como a &amp;ldquo;Taxa de Acertos de Cache&amp;rdquo; (Cache Hit Ratio), denotada por $C$, onde $0 \le C \le 1$.
Seja a latência até o servidor de origem $L_{origin}$, e a latência até o nó de borda mais próximo $L_{edge}$.&lt;/p>
&lt;p>A nova latência média $L_{new}$ é calculada como o seguinte valor esperado:&lt;/p>
$$ L_{new} = C \times L_{edge} + (1 - C) \times (L_{edge} + L_{origin}) $$&lt;p>Simplificando esta fórmula, temos:&lt;/p>
$$ L_{new} = L_{edge} + (1 - C) \times L_{origin} $$&lt;p>Por exemplo, se um usuário em Tóquio acessar um servidor de origem localizado na costa leste dos Estados Unidos (Nova York), levando em conta a distância física da fibra óptica e o atraso de processamento nos roteadores, $L_{origin}$ será de aproximadamente 200 ms. Por outro lado, utilizando uma CDN como a Cloudflare, é possível conectar-se ao nó de borda de Tóquio, reduzindo $L_{edge}$ para cerca de 10 ms.&lt;/p>
&lt;p>Supondo uma taxa de acertos de cache $C = 0.95$ (95%),&lt;/p>
$$ L_{new} = 10 + (1 - 0.95) \times 200 = 10 + 0.05 \times 200 = 10 + 10 = 20 \text{ ms} $$&lt;p>Dessa forma, com a introdução da CDN, torna-se possível reduzir drasticamente (em cerca de 90%) a latência média, de 210 ms para 20 ms.&lt;/p>
&lt;hr>
&lt;h2 id="4-construção-de-um-pipeline-cicd-com-github-actions">4. Construção de um Pipeline CI/CD com GitHub Actions
&lt;/h2>&lt;p>Para automatizar o processo de atualização de um blog Hugo, construiremos um pipeline CI/CD utilizando o GitHub Actions. Com isso, basta escrever artigos em Markdown localmente e dar um &lt;code>git push&lt;/code> para que o processo de compilação execute automaticamente e o site seja implantado no Cloudflare Pages ou GitHub Pages.&lt;/p>
&lt;p>O diagrama de sequência a seguir mostra todo o fluxo, desde o envio do artigo (Push) até sua entrega aos usuários.&lt;/p>
&lt;pre class="mermaid">
sequenceDiagram
participant U as &amp;#34;Usuário (Autor)&amp;#34;
participant G as &amp;#34;Repositório 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;Visitante&amp;#34;
U-&amp;gt;&amp;gt;G: &amp;#34;git push origin main&amp;#34;
G-&amp;gt;&amp;gt;A: &amp;#34;Acionar Evento Push&amp;#34;
A-&amp;gt;&amp;gt;A: &amp;#34;Checkout e Configurar Hugo&amp;#34;
A-&amp;gt;&amp;gt;A: &amp;#34;hugo --minify (Build)&amp;#34;
A-&amp;gt;&amp;gt;C: &amp;#34;Implantar no Cloudflare Pages (Upload Direto)&amp;#34;
C--&amp;gt;&amp;gt;A: &amp;#34;URL de Implantação (Sucesso)&amp;#34;
V-&amp;gt;&amp;gt;C: &amp;#34;Solicitar Página Web&amp;#34;
C--&amp;gt;&amp;gt;V: &amp;#34;Retornar HTML em Cache (Borda)&amp;#34;
&lt;/pre>
&lt;h3 id="41-configuração-de-implantação-para-o-cloudflare-pages-upload-direto">4.1 Configuração de Implantação para o Cloudflare Pages (Upload Direto)
&lt;/h3>&lt;p>O Cloudflare Pages possui dois métodos: vincular um repositório GitHub para que a compilação ocorra na infraestrutura da Cloudflare, ou fazer o &amp;ldquo;Direct Upload&amp;rdquo; (Upload Direto) dos arquivos estáticos previamente compilados com o GitHub Actions. Se você deseja controlar a versão do Hugo com mais rigor e vincular o processo a outras tarefas (testes e otimização de imagens), é recomendável o método de compilação no GitHub Actions e Upload Direto.&lt;/p>
&lt;p>Abaixo, um exemplo prático do arquivo &lt;code>.github/workflows/deploy.yml&lt;/code> para implantação no 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;your-project-name&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>Neste pipeline, utilizamos a opção &lt;code>--minify&lt;/code> para minificar HTML/CSS/JS e &lt;code>--gc&lt;/code> para remover arquivos desnecessários. Esses são os fundamentos da otimização de desempenho.&lt;/p>
&lt;hr>
&lt;h2 id="5-mergulho-profundo-nas-configurações-de-dns-domínios-personalizados-e-registros-cname--alias">5. Mergulho Profundo nas Configurações de DNS: Domínios Personalizados e Registros CNAME / ALIAS
&lt;/h2>&lt;p>Ao utilizar um domínio personalizado (ex: &lt;code>kenji.blog&lt;/code>), a configuração adequada do DNS (Domain Name System) é essencial.&lt;/p>
&lt;h3 id="51-restrições-dos-registros-cname-e-zone-apex">5.1 Restrições dos Registros CNAME e Zone Apex
&lt;/h3>&lt;p>Normalmente, para apontar um subdomínio (ex: &lt;code>www.kenji.blog&lt;/code>) para um serviço externo, usa-se um registro &lt;code>CNAME&lt;/code>. Contudo, devido à especificação do DNS (RFC 1034), não é possível definir um registro &lt;code>CNAME&lt;/code> no domínio raiz (também conhecido como Zone Apex ou naked domain, ex: &lt;code>kenji.blog&lt;/code>). Isso ocorre porque o Zone Apex deve, obrigatoriamente, conter um registro SOA (Start of Authority), registros NS (Name Server) ou MX (Mail Exchange), e há uma regra de que o CNAME não pode coexistir com outros registros de recurso.&lt;/p>
&lt;h3 id="52-a-solução-alias--aname--cname-flattening">5.2 A Solução: ALIAS / ANAME / CNAME Flattening
&lt;/h3>&lt;p>Para resolver esse problema, os provedores de DNS modernos oferecem suas próprias extensões:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Registros ALIAS / ANAME&lt;/strong>: O servidor DNS realiza dinamicamente a resolução de nomes no lado do servidor e retorna os registros A finais (endereços IP) para o cliente. Serviços como o Amazon Route 53 suportam isso.&lt;/li>
&lt;li>&lt;strong>CNAME Flattening&lt;/strong>: Um recurso fornecido pela Cloudflare. Ele atua como se um CNAME fosse configurado para a Zone Apex, mas o servidor DNS autoritativo da Cloudflare retorna de forma transparente para o cliente o conjunto de endereços IP (registros A e AAAA) que foram resolvidos automaticamente.&lt;/li>
&lt;/ul>
&lt;p>Ao utilizar o Cloudflare Pages, delegar os servidores de nomes (nameservers) do seu domínio para a Cloudflare e aproveitar este &amp;ldquo;CNAME Flattening&amp;rdquo; é a configuração mais perfeita e de maior desempenho.&lt;/p>
&lt;hr>
&lt;h2 id="6-estratégias-de-cache-e-controle-de-cabeçalhos-http">6. Estratégias de Cache e Controle de Cabeçalhos HTTP
&lt;/h2>&lt;p>Outro pilar na aceleração de sites estáticos é a &amp;ldquo;Estratégia de Cache&amp;rdquo;. Com o Cloudflare Pages, utilizando o arquivo gerado (arquivo &lt;code>_headers&lt;/code>), é possível ter um controle preciso dos cabeçalhos HTTP de resposta.&lt;/p>
&lt;h3 id="61-cache-de-borda-edge-cache-vs-cache-do-navegador-browser-cache">6.1 Cache de Borda (Edge Cache) vs Cache do Navegador (Browser Cache)
&lt;/h3>&lt;p>O cache é amplamente dividido em dois tipos: o &amp;ldquo;Edge Cache&amp;rdquo; mantido pelo lado da CDN e o &amp;ldquo;Browser Cache&amp;rdquo; armazenado no navegador do usuário.&lt;/p>
&lt;p>Para os arquivos estáticos (como imagens, CSS e JS que contêm hashes no nome do arquivo), o ideal é que eles sejam mantidos no cache do navegador por longos períodos. Por outro lado, para arquivos HTML, a fim de que as atualizações sejam refletidas imediatamente, a prática comum é ter um tempo de cache de navegador muito curto (ou até mesmo desativado), lidando com essas requisições por meio do edge cache.&lt;/p>
&lt;p>Exemplo de configuração do arquivo &lt;code>_headers&lt;/code> no 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"># Arquivos HTML não usarão o cache do navegador e serão validados sempre
&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"># Arquivos de recursos (CSS/JS/Imagens) ficarão no cache do navegador por 1 ano
&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-fórmula-para-cálculo-de-redução-dos-custos-com-largura-de-banda">6.2 Fórmula para Cálculo de Redução dos Custos com Largura de Banda
&lt;/h3>&lt;p>Ao configurar cabeçalhos de cache apropriados, o volume de transferência de dados do servidor (borda) pode ser drasticamente reduzido. O custo mensal com largura de banda $Cost$ pode ser expresso pelo seguinte modelo, dependendo do volume de transferência $B_i$ de cada recurso, da taxa de acertos do cache $C_i$ e do preço unitário da largura de banda $R$:&lt;/p>
$$ Cost = \sum_{i=1}^{n} \left( B_i \times (1 - C_i) \times R \right) $$&lt;p>Como a taxa de transferência de saída da Cloudflare é gratuita ($R = 0$), o custo monetário direto é $0$. Porém, ao utilizar outras infraestruturas simultaneamente como o GitHub Pages, ou usar serviços como o AWS S3 como backend, maximizar essa taxa de acertos de cache $C_i$ se torna o pilar para a redução dos custos da infraestrutura.&lt;/p>
&lt;hr>
&lt;h2 id="7-análise-de-acesso-que-equilibra-privacidade-e-desempenho">7. Análise de Acesso que Equilibra Privacidade e Desempenho
&lt;/h2>&lt;p>Ao gerir um blog, a análise de acesso (Web Analytics) é essencial para saber quantos usuários o estão visitando. O Google Analytics (GA4) foi o padrão da indústria por muito tempo, mas com a recente tendência voltada para a proteção de privacidade (GDPR, CCPA) e a depreciação dos cookies de terceiros, o cenário está mudando.&lt;/p>
&lt;h3 id="71-impacto-no-desempenho-da-web">7.1 Impacto no Desempenho da Web
&lt;/h3>&lt;p>A adoção do Google Analytics (especificamente o &lt;code>gtag.js&lt;/code> e o Google Tag Manager) resulta no carregamento e execução de inúmeros scripts externos, prejudicando o desempenho (especialmente o tempo de TTFB e o tempo de bloqueio da thread principal).&lt;/p>
&lt;p>Vamos tentar dividir e considerar o tempo de carregamento de um site da seguinte maneira:&lt;/p>
&lt;pre class="mermaid">
pie title &amp;#34;Repartição Típica do Tempo de Carregamento do Site&amp;#34;
&amp;#34;TTFB (Time to First Byte)&amp;#34; : 15
&amp;#34;Download de Conteúdo (HTML/CSS)&amp;#34; : 20
&amp;#34;DOM Parsing &amp;amp; Renderização&amp;#34; : 45
&amp;#34;Analytics &amp;amp; JS de Terceiros&amp;#34; : 20
&lt;/pre>
&lt;p>Não é raro que ferramentas analíticas JS de terceiros representem entre cerca de 20% a 30% do tempo total de carregamento.&lt;/p>
&lt;h3 id="72-implementando-o-cloudflare-web-analytics">7.2 Implementando o Cloudflare Web Analytics
&lt;/h3>&lt;p>Assim, ferramentas de análises de acesso que colocam a privacidade em primeiro lugar e não utilizam cookies (Cookieless), como o Cloudflare Web Analytics e o Plausible Analytics, vêm chamando a atenção.&lt;/p>
&lt;p>O Cloudflare Web Analytics funciona apenas com a incorporação de um snippet de JavaScript extremamente leve e, como não emite cookies, evita a necessidade de colocar os incômodos banners de consentimento de cookies (Cookie Consent Banner).&lt;/p>
&lt;p>A implementação no Hugo também é muito simples. Basta adicionar o snippet fornecido nos arquivos &lt;code>layouts/partials/head.html&lt;/code> ou &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;YOUR_CLOUDFLARE_BEACON_TOKEN&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>Adicionando o atributo &lt;code>defer&lt;/code>, o script é carregado de forma assíncrona sem bloquear a análise (parsing) do HTML e executado após a construção do DOM. Dessa forma, é possível minimizar os impactos na velocidade de renderização inicial (LCP: Largest Contentful Paint e FCP: First Contentful Paint).&lt;/p>
&lt;hr>
&lt;h2 id="8-conclusão-e-melhores-práticas">8. Conclusão e Melhores Práticas
&lt;/h2>&lt;p>Na operação de sites estáticos usando o Hugo, a adoção de plataformas de hospedagem modernas como o Cloudflare Pages e o GitHub Pages possui vantagens esmagadoras em termos de relação custo-benefício, velocidade de exibição e segurança.&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Compilação Extremamente Rápida&lt;/strong>: Aproveitar a velocidade do Hugo para minimizar o tempo de execução do pipeline de CI/CD (GitHub Actions).&lt;/li>
&lt;li>&lt;strong>Distribuição na Borda&lt;/strong>: Usar a rede de borda da Cloudflare para entregar conteúdo a usuários em todo o mundo com latência de milissegundos.&lt;/li>
&lt;li>&lt;strong>Configuração Apropriada de DNS&lt;/strong>: Aproveitar o CNAME Flattening para operar a Zone Apex (seu domínio personalizado) de forma segura e rápida.&lt;/li>
&lt;li>&lt;strong>Otimização de Estratégias de Cache&lt;/strong>: Usar o arquivo &lt;code>_headers&lt;/code> para separar adequadamente o cache de navegador do cache de borda, dependendo do tipo de recurso.&lt;/li>
&lt;li>&lt;strong>Analytics Mais Leves&lt;/strong>: Implementar opções como o Cloudflare Web Analytics, que respeitam a privacidade enquanto não comprometem o desempenho.&lt;/li>
&lt;/ol>
&lt;p>Combinando tudo isso, é possível construir de forma gratuita um sistema de blog escalável e robusto, capaz de suportar tráfegos de larga escala (de milhões de pageviews mensais). Se você está considerando iniciar um blog de tecnologia, um site corporativo ou um portfólio, por favor não hesite em testar essa configuração composta por Jamstack, Hugo e Cloudflare Pages.&lt;/p></description></item></channel></rss>