<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tutoriais | AkitaOnRails.com</title><link>https://akitaonrails.github.io/tags/tutoriais/</link><description>Blog do Fabio Akita do Canal do YouTube 'Akitando' falando sobre tecnologia, carreira e coisas geek.</description><generator>Hugo -- gohugo.io</generator><language>pt-BR</language><lastBuildDate>Mon, 19 Oct 2026 23:20:49 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/tags/tutoriais/index.xml" rel="self" type="application/rss+xml"/><item><title>Por que uma eleição digital perfeita ainda não seria viável?</title><link>https://akitaonrails.github.io/2026/08/07/por-que-uma-eleicao-digital-perfeita-ainda-nao-seria-viavel/</link><guid isPermaLink="true">https://akitaonrails.github.io/2026/08/07/por-que-uma-eleicao-digital-perfeita-ainda-nao-seria-viavel/</guid><pubDate>Fri, 07 Aug 2026 10:00:00 -0300</pubDate><description>&lt;p&gt;Toda eleição no Brasil vira a mesma novela: metade do país não confia no resultado. E, diferente da maioria dos países, aqui não tem o que recontar. O Brasil é um dos poucos países do mundo com eleição &lt;strong&gt;100% digital&lt;/strong&gt;: sem recibo de papel, sem cédula física, sem possibilidade de recontagem independente. O voto entra numa urna eletrônica, vira um número dentro de um software fechado e sai um boletim. Ou você confia no TSE, ou não tem o que fazer.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Toda eleição no Brasil vira a mesma novela: metade do país não confia no resultado. E, diferente da maioria dos países, aqui não tem o que recontar. O Brasil é um dos poucos países do mundo com eleição <strong>100% digital</strong>: sem recibo de papel, sem cédula física, sem possibilidade de recontagem independente. O voto entra numa urna eletrônica, vira um número dentro de um software fechado e sai um boletim. Ou você confia no TSE, ou não tem o que fazer.</p>
<p>Recentemente o TSE fez um teatrinho: <a href="https://www.tse.jus.br/comunicacao/noticias/2026/Junho/eleicoes-2026-tse-abre-urna-eletronica-para-tecnicos-da-sociedade-brasileira-de-computacao"target="_blank" rel="noopener">&ldquo;abriu a urna&rdquo;</a> pra técnicos da Sociedade Brasileira de Computação verem os componentes. Isso é obviamente inútil. Mostrar placa-mãe, processador e memória não prova que não existe malware. Hardware é só o palco; a peça acontece no software. E auditar o software é justamente o que é difícil, restrito, cheio de ritual e janela curta — o que derrota completamente o propósito de transparência pública. Uma auditoria que só meia dúzia de credenciados consegue fazer, sob supervisão, por alguns dias, não é transparência. É encenação.</p>
<p>E pra deixar claro: eu não defendo o sistema atual, nem tenho expectativa nenhuma nele. Como resumi <a href="https://x.com/AkitaOnRails/status/2084669984555331706"target="_blank" rel="noopener">neste tweet</a>: a urna é só uma caixa, um PC velho. Mesmo que o software fosse perfeito, não faria diferença — os processos ao redor continuam secretos, feitos debaixo dos panos. A urna é cortina de fumaça. Com ou sem alternativa, eu não ligo pra ela.</p>
<p>Muita gente conclui daí que o certo seria ter eleição digital, mas com um sistema diferente. Esse artigo é um exercício de ciência da computação: como seria um sistema hipoteticamente perfeito? E, mais importante, a conclusão final: <strong>por que mesmo esse sistema perfeito não seria uma opção viável.</strong></p>
<p>Uma coisa que vale enfatizar antes de começar: o sistema que vou descrever não exigiria <strong>nenhum</strong> segredo do governo. Nenhum componente secreto, nenhum processo secreto, nenhuma sala trancada com credenciado vigiando. Tudo — o código, os dados, a estrutura inteira — poderia ser 100% aberto, acessível pra qualquer pessoa, sem restrição nenhuma. E ainda assim seria possível provar, matematicamente, que fraudar é impossível. É exatamente o oposto do modelo atual, em que a confiança nasce do sigilo.</p>
<blockquote>
  <p>Sem paciência pra código e matemática no meio do caminho? <a href="#e-por-que-isso-nunca-funcionaria">Pule direto pra parte em que explico por que isso não funcionaria</a>. E no fim do artigo, três apêndices: <a href="#ap%c3%aandice-1-afinal-o-sistema-atual-%c3%a9-audit%c3%a1vel">o sistema atual é auditável?</a>, <a href="#ap%c3%aandice-2-skin-in-the-game--recompensar-quem-verifica">como dar incentivo pra quem verifica o voto</a> e <a href="#ap%c3%aandice-3-por-que-conhecimento-zero-%c3%a9-t%c3%a3o-dif%c3%adcil-de-engolir">por que &ldquo;conhecimento zero&rdquo; é tão difícil de engolir</a>.</p>

</blockquote>
<h2>&ldquo;A urna é segura porque não está na internet&rdquo;<span class="hx:absolute hx:-mt-20" id="a-urna-é-segura-porque-não-está-na-internet"></span>
    <a href="#a-urna-%c3%a9-segura-porque-n%c3%a3o-est%c3%a1-na-internet" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Todo defensor do sistema atual saca esse argumento mais cedo ou mais tarde: a urna não está conectada à internet, logo não pode ser hackeada de fora. Tecnicamente é verdade. E é completamente irrelevante.</p>
<p>Primeiro, porque isolamento não diz nada sobre o que o software faz. Uma urna offline pode trocar votos do mesmo jeito — você só não tem como assistir ela fazendo isso. E malware não precisa de rede pra chegar: pode vir de fábrica, numa atualização, na cadeia de suprimentos, de um técnico com acesso físico. O Stuxnet, o worm mais famoso da história, atravessou o air gap das centrífugas iranianas via pendrive. Air gap é obstáculo, não é prova de honestidade. E repare: o dado da urna precisa sair dela de algum jeito no fim do dia, em mídia física transportada ou transmissão posterior. &ldquo;Não está na internet&rdquo; é uma meia-verdade logística.</p>
<p>Segundo, e mais importante: esse argumento revela o modelo mental errado. A segurança do sistema atual nasce de <strong>custódia física</strong> — lacres, salas trancadas, credenciados, rituais. Ou seja, mais uma vez, de confiar em gente e em processo que você não vê.</p>
<p>No sistema que vou descrever, a urna poderia estar <strong>ligada direto na internet</strong>, publicando cada voto na árvore pública em tempo real, e ainda assim seria impossível fraudar. Não porque a rede seja segura, mas porque ninguém precisa confiar na urna:</p>
<ul>
<li>O que ela publica são compromissos opacos: mesmo transmitindo tudo ao vivo, não existe voto pra vazar nos dados publicados.</li>
<li>Cada compromisso carrega uma prova matemática de validade: a urna não consegue inventar voto inválido.</li>
<li>A árvore é pública e replicada por observadores independentes: nada do que foi publicado pode ser alterado depois sem quebrar os hashes.</li>
<li>E se a urna trocar seu voto na hora, o desafio de Benaloh pega (é a Peça 4, mais adiante): ela não sabe se você vai confirmar ou auditar.</li>
</ul>
<p>Estar ou não na internet deixa de ser a questão. A segurança não mora na ausência de rede; mora na verificação pública. &ldquo;Confie, a máquina está trancada numa sala&rdquo; vira &ldquo;não confie em nada, confira a matemática&rdquo;. Essa inversão é a única coisa que o modelo atual não consegue oferecer.</p>
<h2>O tweet que inspirou este post<span class="hx:absolute hx:-mt-20" id="o-tweet-que-inspirou-este-post"></span>
    <a href="#o-tweet-que-inspirou-este-post" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Recentemente eu postei <a href="https://x.com/AkitaOnRails/status/2085550756837335483"target="_blank" rel="noopener">este tweet</a> sobre verificabilidade ponta a ponta (E2E-V, <em>end-to-end verifiability</em>). O resumo da ideia:</p>
<ol>
<li>Cada eleitor recebe um recibo em papel com um identificador único — um hash — que <strong>não identifica a pessoa nem o voto</strong>.</li>
<li>Os votos de cada urna entram numa estrutura pública onde só se acrescenta, nunca se apaga (<em>append-only</em>): uma Merkle tree (o mesmo princípio de uma blockchain).</li>
<li>Essa árvore é publicada na íntegra. Qualquer cidadão baixa e verifica.</li>
<li><strong>Verificabilidade individual:</strong> cada eleitor checa se o seu hash está lá, sem intermediário.</li>
<li><strong>Verificabilidade universal:</strong> qualquer um refaz a conta da árvore e confere se ela produz o total anunciado.</li>
</ol>
<p>Eu deixei explícito no tweet: isso <strong>não</strong> é uma solução, é uma ideia de guardanapo. O conceito existe, é matematicamente sólido e não é novidade pra ninguém de ciência da computação. Mas a reação foi interessante.</p>
<h2>O problema do voto de cabresto<span class="hx:absolute hx:-mt-20" id="o-problema-do-voto-de-cabresto"></span>
    <a href="#o-problema-do-voto-de-cabresto" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Boa parte dos comentários travou no mesmo ponto: o voto de cabresto. No Brasil isso é prática histórica: o coronel compra o voto do eleitor pobre e exige prova de que ele votou &ldquo;direito&rdquo;. Antigamente era a cédula pré-marcada; hoje seria a foto da tela da urna (por isso celular é proibido na cabine).</p>
<p>E aí surge a aparente contradição:</p>
<ul>
<li>Se o recibo que o eleitor leva pra casa <strong>revela</strong> pra quem ele votou, ele pode ser coagido ou vendido.</li>
<li>Se o recibo <strong>não revela</strong> pra quem ele votou, como o eleitor confere que o voto dele foi pro candidato certo?</li>
</ul>
<p>Recentemente postei a resposta, ainda que só de passagem: <strong>prova de conhecimento zero</strong> (zero-knowledge proof, ZK). O mesmo princípio por trás de criptomoedas verdadeiramente anônimas como Monero e Zcash.</p>
<p>O fluxo seria assim: o eleitor escolhe o candidato na tela; a urna gera um número aleatório secreto, computa <code>ciphertext = Enc(chave_publica_da_eleicao, voto; aleatorio)</code>, gera uma prova ZK de que esse ciphertext contém um voto válido, e publica tudo numa Merkle tree pública. O eleitor leva pra casa só um número de série. O serial <strong>não contém o voto</strong>, mas permite provar que o voto existe na árvore e não foi adulterado.</p>
<p>Agora vamos destrinchar isso pra programador, passo a passo, com valores reais — e no final eu explico por que isso, mesmo funcionando perfeitamente, não resolveria nada.</p>
<h2>Peça 1: hash como compromisso<span class="hx:absolute hx:-mt-20" id="peça-1-hash-como-compromisso"></span>
    <a href="#pe%c3%a7a-1-hash-como-compromisso" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A fundação de tudo é a função de hash. Uma função como SHA-256 pega qualquer entrada e produz 32 bytes aparentemente aleatórios. Três propriedades importam aqui:</p>
<ul>
<li><strong>Determinística:</strong> mesma entrada, mesma saída. Sempre.</li>
<li><strong>Unidirecional:</strong> dado o hash, não dá pra voltar à entrada.</li>
<li><strong>Avalanche:</strong> mudar um bit da entrada muda o hash inteiro.</li>
</ul>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">hashlib</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">commit</span><span class="p">(</span><span class="n">voto</span><span class="p">,</span> <span class="n">nonce</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">sha256</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">voto</span><span class="si">}</span><span class="s2">:</span><span class="si">{</span><span class="n">nonce</span><span class="si">}</span><span class="s2">&#34;</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">()</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">commit(&#39;Candidato A&#39;, 987654321) = 337051f1dbc6a8ef412ecc14067c263d6a0dc83dada6939b51d74b6651727b69
</span></span><span class="line"><span class="cl">commit(&#39;Candidato A&#39;, 123456789) = 55512bcd60924abf68d162f1a130023635089974db08ea9cff691e1f209898ab
</span></span><span class="line"><span class="cl">commit(&#39;Candidato B&#39;, 987654321) = faf54e90eb84ba4c0446701c3779a382e48d02847bd2301f3836f673c54f9693</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Olhe com atenção. O primeiro e o segundo hash escondem <strong>o mesmo voto</strong> — o que muda é o <code>nonce</code>, um número aleatório que serve de &ldquo;embalagem&rdquo;. O primeiro e o terceiro têm o mesmo nonce, mas votos diferentes. Os três hashes não têm nenhuma semelhança visível entre si.</p>
<p>Isso é um <strong>compromisso</strong> (<em>commitment</em>): eu publico o hash hoje, e amanhã posso revelar <code>(voto, nonce)</code> e qualquer um confere que o hash bate. Eu não consigo mudar o voto depois de publicado (propriedade <em>binding</em>), e ninguém consegue descobrir o voto antes da revelação (propriedade <em>hiding</em>).</p>
<p>Só que tem um problema pra nossa eleição: se o eleitor leva pra casa o voto e o nonce, ele pode <strong>mostrar os dois pro coagidor</strong>, que verifica o hash e confirma o voto. Cabresto de volta. Precisamos de algo melhor.</p>
<h2>Peça 2: compromissos Pedersen — esconder de verdade<span class="hx:absolute hx:-mt-20" id="peça-2-compromissos-pedersen--esconder-de-verdade"></span>
    <a href="#pe%c3%a7a-2-compromissos-pedersen--esconder-de-verdade" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O compromisso de Pedersen resolve isso com aritmética modular. Vou usar números de brinquedo pra você poder conferir na mão; sistemas reais usam primos de 2048 bits ou curvas elípticas, mas a matemática é idêntica.</p>
<p>Pegue um primo <code>p = 23</code> e dois geradores <code>g = 4</code> e <code>h = 8</code> (ambos geram um subgrupo de ordem 11 módulo 23 — confira: <code>4^11 mod 23 = 1</code> e <code>8^11 mod 23 = 1</code>). O compromisso de um voto <code>v</code> com nonce <code>n</code> é:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C = g^v * h^n  (mod p)</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">p</span><span class="p">,</span> <span class="n">q</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">h</span> <span class="o">=</span> <span class="mi">23</span><span class="p">,</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">8</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">pedersen</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">(</span><span class="nb">pow</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">v</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span> <span class="o">*</span> <span class="nb">pow</span><span class="p">(</span><span class="n">h</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">p</span><span class="p">))</span> <span class="o">%</span> <span class="n">p</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C(voto=1, n=3)  = 1
</span></span><span class="line"><span class="cl">C(voto=1, n=9)  = 13
</span></span><span class="line"><span class="cl">C(voto=0, n=3)  = 6</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Mesmo voto, nonces diferentes, compromissos completamente diferentes. O Pedersen tem uma propriedade chamada <strong>ocultação perfeita</strong> (<em>perfectly hiding</em>): para qualquer compromisso <code>C</code>, <strong>existe</strong> um nonce que abre <code>C</code> como voto 0, e <strong>existe</strong> um nonce que abre <code>C</code> como voto 1. Com nosso primo de brinquedo dá pra provar por força bruta:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">C</span> <span class="o">=</span> <span class="mi">1</span>  <span class="c1"># o compromisso de cima, C(voto=1, n=3)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">11</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">pedersen</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span> <span class="o">==</span> <span class="n">C</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;C abre como voto=0 com nonce </span><span class="si">{</span><span class="n">n</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">pedersen</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span> <span class="o">==</span> <span class="n">C</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;C abre como voto=1 com nonce </span><span class="si">{</span><span class="n">n</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">C=1 abre como voto=0 com nonce 0
</span></span><span class="line"><span class="cl">C=1 abre como voto=1 com nonce 3</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Leia de novo, porque este é o coração do artigo. O compromisso <code>C = 1</code> é compatível com <strong>as duas histórias</strong>. Quem vê só o <code>C</code> não tem como saber qual é a verdadeira — nem por força bruta, nem com computador quântico, porque ambas as aberturas existem matematicamente. O valor <code>C</code> simplesmente não contém a informação do voto.</p>
<p>&ldquo;Mas espera&rdquo;, você diz, &ldquo;então o eleitor pode trocar de voto depois?&rdquo;. Não, e essa é a outra metade da propriedade: o compromisso é <strong>computacionalmente vinculante</strong> (<em>computationally binding</em>). Quem gerou o compromisso com <code>(voto=1, n=3)</code> só consegue revelar a outra abertura (<code>voto=0, n=0</code>) se conseguir calcular logaritmo discreto — ou seja, descobrir <code>x</code> tal que <code>g^x = h mod p</code>. Com <code>p = 23</code> é trivial; com 2048 bits, é computacionalmente impossível. Resumo:</p>
<ul>
<li><strong>Quem olha de fora</strong> não descobre o voto (ocultação).</li>
<li><strong>Quem gerou</strong> não consegue mudar o voto depois (vinculação).</li>
</ul>
<p>E o detalhe final que mata o cabresto: <strong>quem gera o nonce é a urna, não o eleitor.</strong> O eleitor vê seu voto na tela, a urna faz o compromisso internamente, descarta o nonce e imprime só o <code>C</code> — o serial. O eleitor sai da cabine sem ter como abrir o próprio compromisso, nem que queira.</p>
<p>(Um detalhe de implementação que vou simplificar daqui pra frente: o Pedersen esconde tão bem que <strong>ninguém</strong> consegue decriptá-lo — nem as autoridades na hora de apurar. Na prática, a urna publica junto um ciphertext ElGamal do mesmo voto: igualmente opaco pra quem olha de fora, mas decriptável pelas autoridades na apuração, com uma prova ZK de que os dois carregam o mesmo voto. Nosso Pedersen de brinquedo faz o papel dos dois neste artigo, pra manter a matemática com números pequenos.)</p>
<h2>Peça 3: a Merkle tree — a urna que qualquer um audita<span class="hx:absolute hx:-mt-20" id="peça-3-a-merkle-tree--a-urna-que-qualquer-um-audita"></span>
    <a href="#pe%c3%a7a-3-a-merkle-tree--a-urna-que-qualquer-um-audita" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Onde ficam esses compromissos? Numa estrutura pública que qualquer pessoa pode baixar e verificar: uma Merkle tree. Se você assistiu meu vídeo sobre <a href="/2023/11/10/akitando-147-criptografia-na-pratica-certificados-bittorrent-git-bitcoin/">criptografia na prática — certificados, BitTorrent, Git, Bitcoin</a>, já viu essa estrutura em ação: é a mesma que escala o BitTorrent, organiza os commits do Git e as transações de um bloco de Bitcoin.</p>
<p>A construção é simples: cada folha é o hash de um voto (o compromisso <code>C</code> de um eleitor), e cada nó interno é o hash da concatenação dos dois filhos, até sobrar um único hash no topo: a <strong>raiz</strong>.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">H</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">hashlib</span><span class="o">.</span><span class="n">sha256</span><span class="p">(</span><span class="n">x</span><span class="o">.</span><span class="n">encode</span><span class="p">())</span><span class="o">.</span><span class="n">hexdigest</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">eleitores</span> <span class="o">=</span> <span class="p">[(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">7</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">10</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">8</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl"><span class="n">leaves</span> <span class="o">=</span> <span class="p">[</span><span class="n">H</span><span class="p">(</span><span class="sa">f</span><span class="s2">&#34;</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">:</span><span class="si">{</span><span class="n">pedersen</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span><span class="si">}</span><span class="s2">&#34;</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">eleitores</span><span class="p">)]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Com os 8 votos de exemplo, as folhas ficam assim (hashes abreviados):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">eleitor 0: voto=1 nonce=3  -&gt; C=1  -&gt; folha=ef134f2a180ba05d...
</span></span><span class="line"><span class="cl">eleitor 1: voto=0 nonce=7  -&gt; C=12 -&gt; folha=ce356d2f943ea5af...
</span></span><span class="line"><span class="cl">eleitor 2: voto=1 nonce=2  -&gt; C=3  -&gt; folha=8e0375adfc1f4563...
</span></span><span class="line"><span class="cl">eleitor 3: voto=1 nonce=10 -&gt; C=12 -&gt; folha=df284a49f837c454...
</span></span><span class="line"><span class="cl">eleitor 4: voto=0 nonce=5  -&gt; C=16 -&gt; folha=fd6df9e3530cb74f...
</span></span><span class="line"><span class="cl">eleitor 5: voto=1 nonce=1  -&gt; C=9  -&gt; folha=e0e9d38f9ccb7a41...
</span></span><span class="line"><span class="cl">eleitor 6: voto=0 nonce=8  -&gt; C=4  -&gt; folha=e719f7fde83fafda...
</span></span><span class="line"><span class="cl">eleitor 7: voto=1 nonce=4  -&gt; C=8  -&gt; folha=1393ac80e69a8991...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">RAIZ PUBLICA: 48826b6481b574e37156f85e34d877105bc55073fb5a5b981239572a8e7c4b61</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>(Os votos e nonces da tabela aparecem abertos só pra você conferir a conta; na árvore pública existem apenas as folhas — hashes opacos.)</p>
<p>A raiz é o &ldquo;resumo&rdquo; da eleição inteira: 32 bytes que representam todos os votos. Se <strong>um único bit</strong> de um único voto mudar, a raiz muda completamente. Publica-se a raiz e a árvore inteira. Qualquer cidadão, em casa, com código aberto, refaz a árvore e confere se a raiz publicada bate. Isso é a <strong>verificabilidade universal</strong>.</p>
<p>Pra verificação individual, suponha que você é o eleitor 4. Seu recibo é o serial — a folha <code>fd6df9e3530cb74f1f0795b751a43454cab281a431d0558b413e33bba83a4100</code>, que é o hash do seu compromisso <code>C = 16</code> na posição 4 da árvore. Pra provar que ele está na árvore, você não precisa baixar e conferir todos os 8 votos — precisa de apenas <code>log2(8) = 3</code> hashes, o caminho dos &ldquo;irmãos&rdquo; até a raiz:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">merkle_verify</span><span class="p">(</span><span class="n">leaf</span><span class="p">,</span> <span class="n">proof</span><span class="p">,</span> <span class="n">root</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">cur</span> <span class="o">=</span> <span class="n">leaf</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">h_</span><span class="p">,</span> <span class="n">side</span> <span class="ow">in</span> <span class="n">proof</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">cur</span> <span class="o">=</span> <span class="n">H</span><span class="p">(</span><span class="n">h_</span> <span class="o">+</span> <span class="n">cur</span><span class="p">)</span> <span class="k">if</span> <span class="n">side</span> <span class="o">==</span> <span class="s2">&#34;esq&#34;</span> <span class="k">else</span> <span class="n">H</span><span class="p">(</span><span class="n">cur</span> <span class="o">+</span> <span class="n">h_</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">cur</span> <span class="o">==</span> <span class="n">root</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">prova do eleitor 4:
</span></span><span class="line"><span class="cl">  (dir) e0e9d38f9ccb7a41...  (irmão: folha do eleitor 5)
</span></span><span class="line"><span class="cl">  (dir) 3149c3bf17d98fbc...  (irmão: nó dos eleitores 6-7)
</span></span><span class="line"><span class="cl">  (esq) 3b902c849a40619b...  (irmão: nó dos eleitores 0-3)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">verificação local: True
</span></span><span class="line"><span class="cl">tentando folha adulterada: False</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Você pega seu serial, concatena com os 3 hashes da prova na ordem certa, hasheia três vezes e confere se chegou na raiz pública. Se chegou, <strong>é matematicamente impossível</strong> que seu voto não esteja na árvore — porque produzir uma prova falsa exigiria encontrar uma colisão no SHA-256, e ninguém no planeta sabe fazer isso. Se alguém trocar seu voto depois, sua prova deixa de funcionar e você tem a evidência da fraude na mão. Numa eleição real com 150 milhões de votos, a prova teria uns 28 hashes — cabe num recibo de papel ou num QR code.</p>
<p>Repare no que acabou de acontecer: você provou que <strong>uma informação está na árvore pública</strong> e que <strong>ninguém mexeu nela</strong>, carregando pra casa apenas um número de 32 bytes que, sozinho, não diz absolutamente nada sobre o conteúdo. É isso que as pessoas querem dizer com &ldquo;zero knowledge&rdquo; nesse contexto: a verificação acontece sem que o conhecimento (o voto) precise circular.</p>
<h2>Peça 4: o desafio de Benaloh — conferindo a urna na hora<span class="hx:absolute hx:-mt-20" id="peça-4-o-desafio-de-benaloh--conferindo-a-urna-na-hora"></span>
    <a href="#pe%c3%a7a-4-o-desafio-de-benaloh--conferindo-a-urna-na-hora" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Sobrou um ponto cego. A urna mostra na tela &ldquo;voto registrado&rdquo; e imprime seu serial <code>C</code>. Em casa, você confere que <code>C</code> está na árvore. Tudo certo? Nem tanto. E se a urna tiver <strong>mentido</strong> e comprometido outro voto? Na tela ela mostra o candidato que você escolheu, mas por dentro calcula o compromisso de outro. Você jamais perceberia, porque o compromisso é opaco por design. É a propriedade de ocultação trabalhando contra você.</p>
<p>A solução clássica é do criptógrafo Josh Benaloh, e ficou conhecida como <strong>desafio de Benaloh</strong> (ou <em>cast-or-challenge</em>). A ideia: depois que a urna mostra o compromisso <code>C</code> na tela, mas <strong>antes</strong> de você confirmar, existem duas opções:</p>
<ul>
<li><strong>Confirmar</strong>: o voto vale, entra na árvore e o nonce é descartado pra sempre.</li>
<li><strong>Desafiar</strong>: você declara aquela cédula um <strong>voto de teste</strong>. A urna é obrigada a revelar o nonce e o voto que ela pôs dentro do compromisso, e você refaz a conta na hora — num app independente, no seu próprio celular, não no software da urna:</li>
</ul>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># a urna mostrou na tela: C = 1</span>
</span></span><span class="line"><span class="cl"><span class="c1"># você desafiou; a urna revela: voto=1, nonce=3</span>
</span></span><span class="line"><span class="cl"><span class="n">pedersen</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span>   <span class="c1"># True -&gt; a urna comprometeu exatamente o que você escolheu</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Se bate, a urna foi honesta <strong>naquela cédula</strong>. A cédula de teste é anulada, não entra na apuração — o nonce revelado a tornaria legível — e você vota de novo, agora pra valer.</p>
<p>Agora suponha uma urna adulterada, que troca uma fração dos votos. Você escolhe <code>voto=1</code>; ela registra por dentro <code>voto=0</code> e mostra <code>C = 12</code> na tela (<code>pedersen(0, 7) = 12</code>). Se você <strong>confirmar</strong>, a fraude passa batida. Mas se você <strong>desafiar</strong>, a urna está encurralada: precisa revelar um par <code>(voto, nonce)</code> que abra <code>C = 12</code>. O único que ela conhece é <code>(0, 7)</code> — e revelar isso expõe a troca na sua frente: &ldquo;eu votei 1!&rdquo;. Abrir como <code>voto=1</code> exigiria achar um nonce <code>n</code> com <code>pedersen(1, n) = 12</code>, que é o problema do logaritmo discreto de novo. Com nosso primo de brinquedo dá pra achar por força bruta (existe: <code>n = 10</code>), mas com primos de 2048 bits a urna trapaceira simplesmente não consegue produzir a resposta.</p>
<p>E o que fecha a armadilha: a urna <strong>não sabe de antemão</strong> se você vai confirmar ou desafiar. A decisão é sua, tomada depois que o compromisso já foi mostrado. Se uma parcela dos eleitores testa algumas cédulas antes de votar pra valer, uma urna que adultera votos em escala é pega com probabilidade esmagadora. Sistemas reais de votação verificável, como o Helios e o ElectionGuard, usam exatamente esse mecanismo.</p>
<p>E note que isso não fere o sigilo de nada: o nonce revelado é de uma cédula <strong>anulada</strong>, que não conta. O voto que vale continua com o nonce descartado e o compromisso impenetrável.</p>
<h2>Peça 5: prova de conhecimento zero de verdade — Schnorr<span class="hx:absolute hx:-mt-20" id="peça-5-prova-de-conhecimento-zero-de-verdade--schnorr"></span>
    <a href="#pe%c3%a7a-5-prova-de-conhecimento-zero-de-verdade--schnorr" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Falta mais uma peça. Quem garante que cada compromisso na árvore contém um <strong>voto válido</strong> — e não, digamos, <code>voto = 500</code>, que inflaria o resultado? A urna precisa provar que o compromisso abre pra um valor legítimo <strong>sem abrir o compromisso</strong>. Isso é uma prova de conhecimento zero no sentido estrito.</p>
<p>O exemplo canônico, e que dá pra demonstrar com números pequenos, é o protocolo de <strong>Schnorr</strong>: provar que você conhece um segredo <code>s</code> tal que <code>y = g^s mod p</code>, sem revelar <code>s</code>. A intuição antes da matemática: é a caverna de Ali Babá. A caverna tem duas passagens que se encontram numa porta trancada. Você prova que tem a chave entrando por um lado e saindo pelo que o verificador pedir — sem nunca mostrar a chave. Se não tivesse a chave, você só acertaria o pedido por sorte, 50% das vezes; depois de 20 rodadas, a chance de enganar é menor que uma em um milhão.</p>
<p>A versão matemática, com nossos números de brinquedo (<code>p = 23</code>, <code>g = 4</code>, ordem <code>q = 11</code>):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">segredo</span> <span class="o">=</span> <span class="mi">7</span>
</span></span><span class="line"><span class="cl"><span class="n">y</span> <span class="o">=</span> <span class="nb">pow</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">segredo</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span>   <span class="c1"># y = 4^7 mod 23 = 8  (valor público)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 1. compromisso: o provador escolhe r aleatório e envia t</span>
</span></span><span class="line"><span class="cl"><span class="n">r</span> <span class="o">=</span> <span class="mi">3</span>
</span></span><span class="line"><span class="cl"><span class="n">t</span> <span class="o">=</span> <span class="nb">pow</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">r</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span>          <span class="c1"># t = 4^3 mod 23 = 18</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. desafio: o verificador escolhe c aleatório</span>
</span></span><span class="line"><span class="cl"><span class="n">c</span> <span class="o">=</span> <span class="mi">5</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. resposta: o provador calcula z</span>
</span></span><span class="line"><span class="cl"><span class="n">z</span> <span class="o">=</span> <span class="p">(</span><span class="n">r</span> <span class="o">+</span> <span class="n">c</span> <span class="o">*</span> <span class="n">segredo</span><span class="p">)</span> <span class="o">%</span> <span class="n">q</span>  <span class="c1"># z = (3 + 5*7) mod 11 = 5</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># verificação: g^z == t * y^c (mod p)</span>
</span></span><span class="line"><span class="cl"><span class="n">esq</span> <span class="o">=</span> <span class="nb">pow</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">z</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span>                 <span class="c1"># 4^5 mod 23 = 12</span>
</span></span><span class="line"><span class="cl"><span class="nb">dir</span> <span class="o">=</span> <span class="p">(</span><span class="n">t</span> <span class="o">*</span> <span class="nb">pow</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">c</span><span class="p">,</span> <span class="n">p</span><span class="p">))</span> <span class="o">%</span> <span class="n">p</span>       <span class="c1"># 18 * 8^5 mod 23 = 12</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="n">esq</span> <span class="o">==</span> <span class="nb">dir</span><span class="p">)</span>                  <span class="c1"># True</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Funciona porque <code>g^z = g^(r + c·s) = g^r · (g^s)^c = t · y^c</code>. A álgebra fecha. Agora observe o que o verificador viu: os números <code>t</code>, <code>c</code>, <code>z</code> e a conferência final. Em nenhum momento o segredo <code>s = 7</code> apareceu. E um impostor que não sabe <code>s</code> não consegue responder a um desafio arbitrário: se ele chutar <code>z = 2</code>, o verificador calcula <code>g^2 = 16 ≠ 12</code> e a fraude aparece.</p>
<p>E por que o verificador aprende <strong>nada</strong> sobre <code>s</code>, e não apenas &ldquo;pouco&rdquo;? Porque a conversa inteira poderia ter sido fabricada por alguém que <strong>não sabe o segredo</strong>, nesta ordem invertida:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># simulador: escolhe c e z PRIMEIRO, depois calcula t que fecha a equação</span>
</span></span><span class="line"><span class="cl"><span class="n">c_sim</span><span class="p">,</span> <span class="n">z_sim</span> <span class="o">=</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">9</span>
</span></span><span class="line"><span class="cl"><span class="n">t_sim</span> <span class="o">=</span> <span class="p">(</span><span class="nb">pow</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">z_sim</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span> <span class="o">*</span> <span class="nb">pow</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="o">-</span><span class="n">c_sim</span> <span class="o">%</span> <span class="n">q</span><span class="p">,</span> <span class="n">p</span><span class="p">))</span> <span class="o">%</span> <span class="n">p</span>  <span class="c1"># t = 8</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># conferência do transcrito forjado:</span>
</span></span><span class="line"><span class="cl"><span class="nb">pow</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="n">z_sim</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span>              <span class="c1"># 4^9 mod 23 = 13</span>
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="n">t_sim</span> <span class="o">*</span> <span class="nb">pow</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">c_sim</span><span class="p">,</span> <span class="n">p</span><span class="p">))</span> <span class="o">%</span> <span class="n">p</span>  <span class="c1"># 8 * 8^5 mod 23 = 13  -&gt; bate!</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O transcrito forjado <code>(t=8, c=5, z=9)</code> passa na verificação e é <strong>indistinguível</strong> de um transcrito real. Se qualquer um consegue fabricar uma conversa válida sem saber o segredo, então a conversa real não pode conter informação nenhuma sobre o segredo. É isso que &ldquo;conhecimento zero&rdquo; significa formalmente. (Pra usar fora de um laboratório, o desafio <code>c</code> é derivado por hash do compromisso — a transformação Fiat-Shamir — e a prova vira um objeto único, não-interativo, que qualquer um verifica offline.)</p>
<p>Na nossa eleição hipotética, a urna publica junto com cada voto uma prova desse tipo — na prática, uma variante em disjunção (&ldquo;o voto é 0 <strong>ou</strong> é 1&rdquo;, sem dizer qual) — e qualquer auditor verifica que todo voto na árvore é válido, sem nunca ver voto nenhum.</p>
<h2>Peça 6: quem impede os votos fantasmas?<span class="hx:absolute hx:-mt-20" id="peça-6-quem-impede-os-votos-fantasmas"></span>
    <a href="#pe%c3%a7a-6-quem-impede-os-votos-fantasmas" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Resta um furo, e é um dos favoritos de quem desconfia de eleição digital: o que impede a autoridade de <strong>enfiar votos inventados na árvore</strong>? Cada compromisso na Merkle tree carrega uma prova ZK de que contém um voto válido — mas a prova só garante que o valor é 0 ou 1. Mil votos fantasmas seriam mil compromissos perfeitamente válidos, com provas perfeitamente corretas. A árvore é pública, qualquer um pode contar as folhas&hellip; mas quem garante que deveriam ser 8 e não 8 mil?</p>
<p>A primeira resposta é a replicação. A árvore segue o modelo das <em>transparency logs</em> — o mesmo Certificate Transparency que protege os certificados HTTPS que você usa o dia inteiro: um publicador escreve, e dezenas de observadores independentes (partidos, OAB, universidades, imprensa, você) copiam a árvore, assinam a raiz e conferem entre si. Reescrever o histórico exigiria convencer todos eles a aceitar uma raiz diferente da que já assinaram.</p>
<p>Isso protege o que já foi publicado. Mas e a <strong>criação</strong> de votos fantasmas? Aí entra a peça mais elegante de todas, e ela é do mesmo David Chaum que inventou o dinheiro digital e as mix-nets: a <strong>assinatura cega</strong> (<em>blind signature</em>, 1982).</p>
<p>O fluxo: você se autentica na mesa como hoje — documento, biometria. Na cabine, a urna calcula seu compromisso (Peça 2), mas antes de publicá-lo precisa de uma credencial da mesa. Ela <strong>cega</strong> o compromisso com um fator aleatório e manda pra mesa só o valor cego — e aqui estar online não é problema nenhum, como vimos na seção do air gap. A mesa, que já registrou sua autenticação, assina uma única vez, sem ver o conteúdo. A urna descega a assinatura e publica o par <code>(compromisso, assinatura)</code>. No fim, a mesa não faz ideia de qual compromisso assinou — logo, não consegue vincular sua identidade ao seu voto. Com RSA de brinquedo, dá pra ver a mágica acontecendo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="c1"># RSA de brinquedo da mesa: n=3233, e=17 (público), d=2753 (privado)</span>
</span></span><span class="line"><span class="cl"><span class="n">n</span><span class="p">,</span> <span class="n">e</span><span class="p">,</span> <span class="n">d</span> <span class="o">=</span> <span class="mi">3233</span><span class="p">,</span> <span class="mi">17</span><span class="p">,</span> <span class="mi">2753</span>
</span></span><span class="line"><span class="cl"><span class="n">C</span> <span class="o">=</span> <span class="mi">16</span>        <span class="c1"># seu compromisso Pedersen, o mesmo da Peça 3</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 1. a urna cega o compromisso com um fator aleatório r</span>
</span></span><span class="line"><span class="cl"><span class="n">r</span> <span class="o">=</span> <span class="mi">7</span>
</span></span><span class="line"><span class="cl"><span class="n">cego</span> <span class="o">=</span> <span class="p">(</span><span class="n">C</span> <span class="o">*</span> <span class="nb">pow</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">e</span><span class="p">,</span> <span class="n">n</span><span class="p">))</span> <span class="o">%</span> <span class="n">n</span>       <span class="c1"># 2341 -&gt; só isso vai pra mesa</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 2. a mesa, que já registrou sua autenticação, assina o valor CEGO</span>
</span></span><span class="line"><span class="cl"><span class="n">ass_cego</span> <span class="o">=</span> <span class="nb">pow</span><span class="p">(</span><span class="n">cego</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span> <span class="o">%</span> <span class="n">n</span>      <span class="c1"># 216</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 3. a urna remove o fator cego e publica (C, s) na árvore</span>
</span></span><span class="line"><span class="cl"><span class="n">s</span> <span class="o">=</span> <span class="p">(</span><span class="n">ass_cego</span> <span class="o">*</span> <span class="nb">pow</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">n</span><span class="p">))</span> <span class="o">%</span> <span class="n">n</span>  <span class="c1"># 2802</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># qualquer auditor verifica: s^e mod n == C ?</span>
</span></span><span class="line"><span class="cl"><span class="nb">print</span><span class="p">(</span><span class="nb">pow</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">e</span><span class="p">,</span> <span class="n">n</span><span class="p">))</span>   <span class="c1"># 16 -&gt; o compromisso, com assinatura válida!</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Repare: a mesa só viu o número 2341, que pra ela é ruído. Ela assinou 216 sem saber que estava assinando o seu <code>C = 16</code>. Mesmo assim, o par <code>(C, s)</code> passa na verificação de qualquer auditor. É uma assinatura digital normal — <code>s</code> é exatamente <code>C^d mod n</code> — só que produzida sem que o signatário visse o documento. Parece truque de mágico, mas é só álgebra do RSA: cegar multiplica por <code>r^e</code>; assinar eleva tudo a <code>d</code>, e como <code>e·d ≡ 1</code>, sobra <code>C^d · r</code>; descegar divide por <code>r</code> e resta <code>C^d</code>.</p>
<p>Agora monte o quadro completo: <strong>uma folha só entra na árvore se vier com duas coisas — a prova ZK de validade (Peça 5) e uma credencial assinada pela mesa (Peça 6).</strong> A mesa emite uma credencial por autenticação, e autenticação é evento público: tem fila na seção, mesário, fiscal de partido, log biométrico, contagem de comparecimento publicada por seção. No fim do dia, a conta que qualquer um confere é:</p>
<p><strong>número de folhas na árvore == número de eleitores autenticados.</strong></p>
<p>Voto fantasma é folha sem credencial (não entra) ou credencial sem eleitor (infla a contagem e aparece). Remover um voto legítimo quebra a prova de inclusão do recibo (Peça 3). Alterar um voto publicado quebra os hashes nas cópias dos observadores. Roubar credenciais exigiria fraudar a biometria na frente dos fiscais. Cada caminho de fraude bate numa parede diferente — e todas as paredes são verificáveis por qualquer um, de casa.</p>
<h2>O elo mais fraco: quem prova que você é você?<span class="hx:absolute hx:-mt-20" id="o-elo-mais-fraco-quem-prova-que-você-é-você"></span>
    <a href="#o-elo-mais-fraco-quem-prova-que-voc%c3%aa-%c3%a9-voc%c3%aa" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Tudo o que veio antes desta seção é matemática. Mas tem uma porta que nenhuma matemática tranca: a autenticação. Nenhum algoritmo prova que o ser humano parado na sua frente é quem diz ser. Esse é, honestamente, o elo mais fraco do sistema inteiro — e vale encarar de frente.</p>
<p>E aqui o Brasil simplifica o problema, porque já pagou a parte cara: o cadastro biométrico do eleitorado existe e cobre mais de 80% dos eleitores — mais de 124 milhões de pessoas, <a href="https://www.tse.jus.br/comunicacao/noticias/2024/Fevereiro/eleitores-sem-cadastro-biometrico-podem-votar-normalmente"target="_blank" rel="noopener">segundo o TSE</a>. A deduplicação nacional já rodou: um mesmo dedo só existe uma vez no cadastro, por mais que venha acompanhado de dez documentos falsos. E na seção a digital já libera o voto — em 2026, <a href="https://www.tre-mg.jus.br/comunicacao/noticias/2026/Julho/manual-do-eleitor-entenda-como-vai-funcionar-a-biometria-nas-eleicoes-2026"target="_blank" rel="noopener">em todas as seções do país</a>. Pro sistema hipotético deste artigo, a autenticação biométrica é de graça: é reusar o que já está de pé.</p>
<p>Claro que esse cadastro existir é, ele mesmo, a aposta de privacidade: biometria é dado irrevogável — senha você troca, sua digital não. O Aadhaar, o cadastro biométrico da Índia, teve incidentes que expuseram dados de mais de um bilhão de pessoas; a OPM, o RH do governo americano, perdeu 5,6 milhões de impressões digitais num único ataque, em 2015. Um banco de dados biométrico nacional é um ponto único de falha catastrófica e permanente — e o Brasil já fez essa aposta. O sistema hipotético não a piora; só herda.</p>
<p>Pra quem está fora do cadastro biométrico, a seção cai pro documento com foto — e documento se falsifica. Provavelmente se falsifica hoje. Mas repare no que a arquitetura faz com esse problema: uma autenticação forjada emite <strong>uma</strong> credencial, que gera <strong>uma</strong> folha na árvore. Pra fraudar em escala, o criminoso precisa de gente de carne e osso se apresentando pessoalmente, na frente de mesário e fiscal, uma vez por voto falso. É fraude de varejo: cara, visível, lenta. Mil votos falsos exigem mil corpos — e aí já não é fraude digital silenciosa, é logística de ônibus fretado que todo mundo vê passar. O sistema não impede a falsificação; ele <strong>limita o estrago por falsificação a exatamente um voto</strong> e empurra o custo pro mundo físico.</p>
<p>E as anomalias aparecem: a equação da Peça 6 — folhas == autenticações — pode ser conferida seção por seção. Uma seção com 400 eleitores cadastrados e 420 autenticações chama atenção. Padrões regionais de comparecimento acima de 100% chamam atenção. E se a paranoia for grande, a chave de assinatura da mesa pode ser fragmentada como a chave da eleição, exigindo quorum pra emitir credencial.</p>
<p>Resumindo com honestidade: o elo continua físico e humano — um humano provando, uma vez, em público, que está no cadastro — e nenhum sistema, por mais perfeito que seja, o elimina. O melhor desenho possível faz duas coisas com ele: encolhe a superfície de confiança até esse evento único e torna as falhas dele <strong>visíveis</strong> em vez de silenciosas. Com a biometria valendo em todas as seções em 2026, a falsificação de identidade vira o caminho mais caro e menos escalável de todos — que é exatamente onde um sistema bem desenhado quer que ela esteja. Não tem almoço grátis, e fingir que tem é o tipo de discurso que este artigo inteiro se recusa a fazer.</p>
<h2>Juntando tudo: o protocolo completo<span class="hx:absolute hx:-mt-20" id="juntando-tudo-o-protocolo-completo"></span>
    <a href="#juntando-tudo-o-protocolo-completo" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>A eleição hipoteticamente perfeita ficaria assim:</p>
<ol>
<li><strong>Preparação.</strong> Um grupo de autoridades independentes (TSE, OAB, partidos, sociedade civil) gera em conjunto a chave pública da eleição. A chave privada correspondente fica fragmentada: nenhuma autoridade sozinha consegue decriptar nada; só uma maioria agindo junta.</li>
<li><strong>Autenticação.</strong> O eleitor se identifica na mesa — documento, biometria, como hoje — e a mesa registra a autenticação. Isso autoriza a emissão de <strong>uma</strong> credencial: a assinatura cega de um compromisso, sem que a mesa veja o conteúdo. Nenhum vínculo entre identidade e voto.</li>
<li><strong>Votação.</strong> O eleitor escolhe o candidato na tela. A urna gera um nonce aleatório, calcula o compromisso Pedersen e o ciphertext ElGamal do mesmo voto (o compromisso vira o serial do recibo; o ciphertext é o que será apurado), produz a prova ZK de validade e mostra o compromisso na tela. O eleitor então decide: confirma, e o nonce é descartado — ou desafia, e a urna revela o nonce pra conferência imediata, a cédula é anulada e ele vota de novo. Confirmado, a urna cega o compromisso, a mesa assina sem vê-lo e a urna descega a assinatura.</li>
<li><strong>Publicação.</strong> O compromisso entra numa Merkle tree pública, junto com a prova ZK e a credencial — replicada e assinada por múltiplos observadores independentes. Sem credencial, não entra.</li>
<li><strong>Recibo.</strong> O eleitor leva pra casa um papel com o serial. Ele <strong>não consegue</strong> provar pra ninguém em quem votou — nem que queira, porque não tem o nonce.</li>
<li><strong>Verificação individual.</strong> Em casa, o eleitor baixa a árvore (ou usa qualquer site independente) e confere que seu serial está lá, com a prova de inclusão. Se não estiver, ele tem prova material da fraude.</li>
<li><strong>Verificação universal.</strong> Qualquer cidadão, universidade ou partido refaz a árvore inteira, confere a raiz, valida todas as provas ZK e credenciais, e confere se o número de folhas bate com o total de autenticações publicado por seção.</li>
<li><strong>Apuração.</strong> No fim, os votos encriptados passam por um mix-net (são re-embaralhados e re-encriptados, cortando o vínculo com a posição original) e as autoridades decriptam em conjunto, provando cada passo. O total bate com a raiz pública ou a fraude é evidente.</li>
</ol>
<p>Repare no que mudou em relação ao sistema atual: <strong>não é mais preciso confiar no TSE, na urna, nem em auditor nenhum.</strong> Cada propriedade é verificável individualmente por qualquer pessoa com um computador. É o mesmo princípio que faz o Bitcoin funcionar sem banco central: don&rsquo;t trust, verify.</p>
<p>Antes que alguém anime demais: isso é uma simplificação. Um sistema real precisa ainda resolver credenciais anônimas mais robustas que nossa assinatura cega de brinquedo, disponibilidade da árvore, e uma porção de detalhes operacionais. O ponto aqui é o mecanismo central, não o projeto completo.</p>
<h2>E por que isso nunca funcionaria<span class="hx:absolute hx:-mt-20" id="e-por-que-isso-nunca-funcionaria"></span>
    <a href="#e-por-que-isso-nunca-funcionaria" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Agora a parte que quase ninguém que propõe esses sistemas quer ouvir.</p>
<p>Volta pro começo do artigo e repara no que eu precisei explicar pra chegar até aqui: funções de hash, compromissos, aritmética modular, logaritmo discreto, árvores de Merkle, o desafio de Benaloh, assinaturas cegas, provas de conhecimento zero, simuladores. Com código, com números, com exemplos passo a passo. E mesmo assim eu aposto que uma parcela boa dos leitores — inclusive programadores — chegou até aqui sem ter certeza de que entendeu de verdade por que o esquema é seguro.</p>
<p>E não é por falta de inteligência. É porque a confiança nesse sistema exige entender matemática que a imensa maioria da população nunca vai entender. O único ser humano que pode ter <strong>100% de certeza</strong> de que esse sistema é correto é aquele que consegue verificar as demonstrações matemáticas por conta própria. Todo o resto — 99,9% da população — não estaria <em>verificando</em> coisa nenhuma. Estaria <strong>acreditando</strong> no matemático que diz que funciona.</p>
<p>E aí chegamos na contradição fatal: se um sistema eleitoral exige que o cidadão comum acredite cegamente num especialista que ele não consegue auditar, ele é <strong>exatamente tão opaco quanto o sistema atual do TSE</strong>. A opacidade só trocou de endereço: em vez de confiar no burocrata do tribunal, você confia no criptógrafo. Pra dona Maria, que vende pastel na feira, tanto faz — os dois são &ldquo;um monte de letrinha que eu não entendo&rdquo;. E um sistema que a população não consegue entender é um sistema cuja legitimidade ela nunca vai aceitar. O cético de hoje que diz &ldquo;não confio na urna&rdquo; viraria o cético de amanhã dizendo &ldquo;não confio nessa álgebra&rdquo;.</p>
<p>Uma eleição não é só um problema de engenharia; é um problema de <strong>confiança social</strong>. O padrão-ouro de transparência não é o sistema mais sofisticado — é o sistema que <strong>qualquer pessoa consegue fiscalizar com os próprios olhos</strong>: cédula de papel, urna de vidro, contagem pública na seção, ata na porta. Qualquer um entende papel sendo contado em público. Ninguém precisa acreditar em ninguém.</p>
<p>É por isso que países muito mais ricos e tecnológicos que o Brasil — Alemanha, Holanda, França, a maior parte dos EUA — continuam com papel ou exigem trilha de papel auditável. Não é atraso. É a constatação de que verificabilidade que só especialista entende não é verificabilidade pública.</p>
<h2>Parêntese: o que as criptomoedas provam todos os dias<span class="hx:absolute hx:-mt-20" id="parêntese-o-que-as-criptomoedas-provam-todos-os-dias"></span>
    <a href="#par%c3%aantese-o-que-as-criptomoedas-provam-todos-os-dias" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Antes de concluir, vale um desvio pra responder à pergunta que sempre aparece: &ldquo;mas isso funciona de verdade ou é teoria de guardanapo?&rdquo;. Funciona. E a prova está rodando há quase duas décadas, movimentando bilhões de dólares, sob ataque constante: as blockchains.</p>
<p>Primeiro, desfazendo um mal-entendido: <strong>blockchain não é sinônimo de criptomoeda.</strong> Como o próprio nome diz, é só uma cadeia de blocos. Cada bloco carrega uma Merkle tree de registros e o hash do bloco anterior. No Bitcoin, o cabeçalho do bloco guarda a raiz da árvore de transações, o hash do bloco anterior, um carimbo de tempo e um nonce — aqui, um contador de mineração — que os mineradores variam até o hash do bloco inteiro cair abaixo do alvo de dificuldade — a tal prova de trabalho. É a Peça 3 deste artigo, estendida no tempo: não uma árvore, mas uma cadeia de árvores, cada uma selando a anterior.</p>
<p>O resultado prático é a garantia que interessa aqui: mexa em <strong>uma transação</strong> de um bloco antigo e a raiz da árvore muda, o hash do bloco muda, o elo com o bloco seguinte quebra, e pra esconder isso você precisaria refazer a prova de trabalho de todos os blocos até hoje — enquanto milhares de nós honestos continuam alongando a cadeia verdadeira. Depois que tudo está assinado e selado por hash, adulterar o passado é computacionalmente impossível. É exatamente por isso que o Bitcoin pode ser 100% público: a exposição funciona como proteção, não como risco. Todo mundo tem cópia de tudo, então ninguém reescreve a história.</p>
<p><strong>Mas público não significa anônimo.</strong> Essa é a parte que a maioria confunde. No Bitcoin, toda transação fica visível pra sempre: quais endereços alimentaram, quais receberam, quanto foi. Endereços são pseudônimos — apelidos, não anonimato. E existe uma indústria inteira de análise de cadeia (Chainalysis, Elliptic, TRM) que vive de grudar identidade nesses apelidos:</p>
<ul>
<li><strong>Heurística dos inputs comuns:</strong> se uma transação gasta moedas de vários endereços, quase certamente todos pertencem à mesma carteira. Agrupe-os num cluster.</li>
<li><strong>Detecção de troco:</strong> o output que volta pra um endereço novo costuma ser do próprio pagador.</li>
<li><strong>Ponte com o mundo real:</strong> quando um endereço do cluster encosta numa exchange com KYC, o nome e o CPF se ligam ao cluster inteiro — e ao histórico completo dele, retroativamente.</li>
</ul>
<p>Foi assim que o FBI recuperou parte do resgate do oleoduto Colonial, foi assim que as moedas da Silk Road foram rastreadas anos depois, e é assim que os ~1.128 BTC roubados das ColdCards continuam parados em endereços conhecidos que meio mundo vigia — eu detalhei isso no <a href="/2026/08/01/explorando-o-problema-escandaloso-do-rng-da-coinkite/">artigo sobre o RNG da Coinkite</a>. Se o ladrão mover um satoshi pra uma exchange com KYC, ele se identifica. A cadeia garante integridade, não sigilo.</p>
<p>Pra ter anonimato de verdade, não basta a cadeia: precisa de prova de conhecimento zero. O Monero, por exemplo, combina três técnicas:</p>
<ol>
<li><strong>Assinaturas de anel</strong> (<em>ring signatures</em>): cada gasto é assinado em nome de um grupo de chaves possíveis. A assinatura prova que <strong>uma delas</strong> autorizou, sem revelar qual.</li>
<li><strong>Endereços furtivos</strong> (<em>stealth addresses</em>): o endereço do destinatário nunca aparece na cadeia; cada pagamento cria um endereço descartável, derivado de um segredo compartilhado.</li>
<li><strong>RingCT</strong> (<em>confidential transactions</em>): os valores ficam escondidos em <strong>compromissos Pedersen</strong> — sim, exatamente a Peça 2 deste artigo — e uma prova de alcance em ZK garante que ninguém criou moeda do nada, sem revelar quantia nenhuma.</li>
</ol>
<p>O Zcash segue a mesma filosofia com zk-SNARKs: você prova &ldquo;eu sou dono de uma nota válida e ainda não gasta&rdquo; sem revelar qual nota. A rede verifica a prova, aceita a transação e não aprende remetente, destinatário nem valor.</p>
<p>E aqui o círculo se fecha. Olhe o que o esquema eleitoral deste artigo usa: uma estrutura pública e imutável que qualquer um verifica (a Merkle tree, primo mais simples da blockchain), compromissos Pedersen pra esconder o conteúdo (a mesma primitiva do Monero) e provas ZK pra garantir validade sem revelação (a mesma família do Monero e do Zcash). Nada disso é conjectura de laboratório: são sistemas em produção, auditados, atacados diariamente, protegendo dinheiro de verdade. Uma eleição é, se muito, um problema mais simples — janela curta, um publicador só, verificação offline.</p>
<p>Por isso eu disse lá no começo que a ideia é matematicamente sólida: ela não inventa nada, só compõe peças que já provaram aguentar o mundo real. Sabemos que um sistema de eleição digital verificável ponta a ponta <strong>poderia</strong> ser construído, porque todos os componentes dele já estão construídos e funcionando. O que nos leva de volta ao problema que não é técnico.</p>
<h2>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Deixo claro mais uma vez, como deixei nos tweets: <strong>isso não é uma proposta, é um exercício.</strong> Eu não sei qual é a solução pro problema da confiança eleitoral no Brasil. Se soubesse, estaria publicando paper, não post de blog.</p>
<p>Mas o exercício vale por duas razões. Primeiro, porque mostra que a tecnologia pra ter verificabilidade individual e universal <strong>existe</strong> — quem diz &ldquo;eleição digital é inerentemente inauditável&rdquo; está errado. Segundo, porque mostra o limite real: a fronteira não é técnica, é epistemológica. Um sistema perfeito que ninguém entende falha no mesmo ponto que um sistema imperfeito que ninguém pode auditar.</p>
<p>Se você entendeu cada linha deste artigo, parabéns: você faz parte de uma minoria pequena demais pra carregar uma democracia nas costas.</p>
<h2>Apêndice 1: afinal, o sistema atual é auditável?<span class="hx:absolute hx:-mt-20" id="apêndice-1-afinal-o-sistema-atual-é-auditável"></span>
    <a href="#ap%c3%aandice-1-afinal-o-sistema-atual-%c3%a9-audit%c3%a1vel" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Fica no ar uma pergunta justa: então o sistema atual não tem auditoria nenhuma? Tem sim, e vale ser honesto sobre isso. O que não existe é um tipo específico de auditoria: a que não depende de software. Vamos por partes.</p>
<p>O arsenal atual, resumido e com as fontes:</p>
<ul>
<li><strong>Inspeção de código-fonte.</strong> Partidos, OAB, TCU, Polícia Federal, Ministério Público e universidades podem examinar o código dos sistemas na sede do TSE, meses antes da eleição — o tal <a href="https://www.tse.jus.br/comunicacao/noticias/2025/Novembro/codigos-fonte-do-ciclo-de-transparencia-ao-teste-publico-da-urna"target="_blank" rel="noopener">ciclo de transparência</a>.</li>
<li><strong>Teste Público de Segurança (TPS).</strong> Pesquisadores externos tentam atacar o sistema de verdade. E ele funciona: em 2012, a equipe do professor Diego Aranha desembaralhou o RDV e mostrou que dava pra descobrir em quem cada eleitor votou; em 2017, conseguiram adulterar o software de votação antes da instalação (a história está na <a href="https://revistapesquisa.fapesp.br/a-urna-eletronica-na-maturidade/"target="_blank" rel="noopener">Revista Pesquisa FAPESP</a> e neste <a href="https://www.agencialupa.org/jornalismo/2023/11/27/entenda-como-funciona-o-teste-de-seguranca-das-urnas-eletronicas-feito-pelo-tse/"target="_blank" rel="noopener">resumo da Lupa</a>). As falhas encontradas foram corrigidas depois.</li>
<li><strong>Zerésima, BU e Teste de Integridade.</strong> Antes da votação, a urna imprime o relatório zerado. No fim do dia, imprime o Boletim de Urna, afixado na seção e publicado digitalmente com QR code — qualquer um pode baixar os BUs e somar por conta própria. E uma amostra aleatória de urnas é desviada pra uma votação paralela, filmada, comparando o registro em papel com o digital.</li>
<li><strong>RDV — a &ldquo;recontagem digital&rdquo;.</strong> A urna grava cada voto individual num arquivo embaralhado e anonimizado, publicado depois (<a href="https://www.tse.jus.br/legislacao/compilada/res/2021/resolucao-no-23-673-14-de-dezembro-de-2021"target="_blank" rel="noopener">definição na Resolução TSE 23.673/2021</a>). Partidos e entidades autorizadas podem re-somar os votos e cruzar com os BUs e com a totalização oficial — é o mecanismo oficial apresentado como substituto da recontagem em papel (<a href="https://www.tse.jus.br/eleicoes/historia/processo-eleitoral-brasileiro/votacao/votacao-segura"target="_blank" rel="noopener">explicação do TSE</a>).</li>
<li><strong>Auditorias externas.</strong> Em 2022, o TCU cruzou 9 milhões de registros de 4.577 seções eleitorais com a base de totalização e encontrou zero divergências — probabilidade de fraude &ldquo;próxima de 0%&rdquo; (<a href="https://www.cnnbrasil.com.br/politica/auditoria-do-tcu-diz-que-possibilidade-de-fraude-nas-eleicoes-de-2022-e-proxima-de-0/"target="_blank" rel="noopener">CNN Brasil</a>). O Carter Center observou as eleições de 2022, não registrou evidência de fraude e reconheceu a ampliação das auditorias (<a href="https://www.cartercenter.org/news/pr/2022/brazil-100522-portuguese.pdf"target="_blank" rel="noopener">declaração da missão</a>).</li>
</ul>
<p>Agora o &ldquo;mas&rdquo; que este artigo inteiro constrói: <strong>toda camada dessa lista depende de software ou de cerimônia conduzida pelo próprio TSE.</strong> A comparação BU × RDV prova consistência interna — que os números publicados batem com os números registrados. Ela não prova que os números registrados refletem o que os eleitores apertaram na tela, porque uma urna adulterada produziria BU e RDV adulterados de forma perfeitamente consistente. É o que o próprio Aranha resume: todas as auditorias disponíveis hoje dependem do software; se o programa for adulterado, todas as checagens caem juntas (<a href="https://www1.folha.uol.com.br/poder/2021/06/so-brasil-bangladesh-e-butao-usam-urna-eletronica-sem-comprovante-do-voto-impresso.shtml"target="_blank" rel="noopener">Folha de S.Paulo</a>). E o Teste de Integridade, que cobre uma amostra pequena de urnas, é um primo pobre do desafio de Benaloh da Peça 4: em vez de cada eleitor auditar a própria cédula na hora, quem audita é uma amostra sorteada e conduzida pelo sistema.</p>
<p>Tem ainda o capítulo jurídico: a tentativa de criar um registro em papel verificável pelo eleitor, o &ldquo;voto impresso&rdquo;, foi declarada inconstitucional pelo STF na ADI 4543, por risco ao sigilo do voto, e a tentativa de ressuscitá-la foi barrada de novo em 2020 (<a href="https://conjur.com.br/2020-set-16/supremo-confirma-liminar-impede-volta-voto-impresso/"target="_blank" rel="noopener">Conjur</a>). A ironia é que o esquema deste artigo resolve exatamente o conflito que derrubou o voto impresso: recibo verificável <strong>sem</strong> quebrar o sigilo, via compromissos e provas ZK. A matemática destrava o que o papel não destravou. O que continua travado é todo o resto, como as seções anteriores explicam.</p>
<p>O veredito honesto, então: o sistema brasileiro é auditável de formas digitais e procedurais reais, e nunca se provou fraude que alterasse um resultado em quase 30 anos de urna eletrônica. Mas não existe recontagem independente de software, porque não existe registro físico verificado pelo eleitor. Na linguagem da pesquisa de segurança eleitoral, uma <em>risk-limiting audit</em> de verdade não é possível. A confiança continua depositada no software, nas inspeções prévias e na custódia dos registros digitais — exatamente o que o sistema hipotético deste artigo tenta eliminar.</p>
<h2>Apêndice 2: skin in the game — recompensar quem verifica<span class="hx:absolute hx:-mt-20" id="apêndice-2-skin-in-the-game--recompensar-quem-verifica"></span>
    <a href="#ap%c3%aandice-2-skin-in-the-game--recompensar-quem-verifica" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Uma pergunta que fica no ar depois de montar o esquema inteiro: criptomoedas funcionam porque alguém tem algo a perder. No Bitcoin, o minerador queima energia de verdade; no proof-of-stake, o validador deposita capital que é confiscado se trapacear. E o eleitor? Votar não custa nada, e conferir o recibo em casa dá trabalho de graça. O sistema inteiro depende de gente verificando — e ninguém tem incentivo pra verificar. Dá pra colocar <em>skin in the game</em> no eleitor?</p>
<p>A primeira ideia que vem à cabeça é um depósito: vote, deposite uma quantia, receba de volta depois via imposto de renda. Como desenho de mecanismo, tem lógica — precifica identidade falsa: um milhão de votos fantasmas custaria cem milhões de reais. Na prática, morre em três lugares. É um imposto eleitoral disfarçado, censitário e inconstitucional — a devolução resolve a contabilidade, não o fluxo de caixa de quem não tem o dinheiro pra adiantar. A via &ldquo;imposto de renda&rdquo; não alcança a maioria dos brasileiros pobres, que nem declaram — ou seja, exclui exatamente quem o sistema mais precisa incluir. E criaria na Receita um banco de dados ligando identidade, conta bancária e participação eleitoral, um retrocesso de vigilância maior que o problema que resolve. Tudo isso antes do detalhe plutocrático: segurança precificada em dinheiro é segurança comprável por quem tem dinheiro. Eleição é desenhada pra ser cega a riqueza — um voto por pessoa. Colocar preço na camada de segurança é convidar o rico a ser mais igual que os outros.</p>
<p>A versão que sobrevive a esse crivo inverte o sinal: em vez de cobrar pra votar, <strong>recompensar quem verifica</strong>. O ponto fraco real do esquema é a taxa de verificação individual — se ninguém confere o serial em casa, a prova de inclusão da Peça 3 vira decoração. Então: cada recibo verificado entra num sorteio público e auditável, com a raiz da própria árvore servindo de fonte de aleatoriedade (mais um uso pra ela). Conferiu seu voto na árvore? Concorre a um prêmio. O incentivo aponta exatamente pro comportamento que o sistema precisa — milhões de olhos conferindo a árvore — sem excluir ninguém, sem cobrar nada e sem banco de dados novo, porque o sorteio roda em cima do serial anônimo, não da identidade.</p>
<p>É a lógica do Bitcoin do lado certo: lá, a recompensa por bloco paga pra milhares de máquinas manterem o ledger honesto; aqui, o prêmio pagaria pra milhões de eleitores manterem a eleição honesta. É quase uma <em>proof of verification</em>: a recompensa de bloco paga o trabalho de hashear que sustenta o Bitcoin, e o prêmio pagaria o trabalho de conferir que sustentaria a eleição. Não é proposta — como nada neste artigo —, mas como exercício de desenho de incentivos, é a versão que eu colocaria no papel.</p>
<h2>Apêndice 3: por que &ldquo;conhecimento zero&rdquo; é tão difícil de engolir<span class="hx:absolute hx:-mt-20" id="apêndice-3-por-que-conhecimento-zero-é-tão-difícil-de-engolir"></span>
    <a href="#ap%c3%aandice-3-por-que-conhecimento-zero-%c3%a9-t%c3%a3o-dif%c3%adcil-de-engolir" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se você chegou até aqui com a sensação de que entendeu cada peça individualmente, mas não juraria que entendeu o conjunto, relaxa: você está em ótima companhia. Quando Goldwasser, Micali e Rackoff formalizaram provas de conhecimento zero nos anos 80, a ideia soou paradoxal até pra criptógrafo de profissão. Toda prova que a gente conhece funciona <em>mostrando</em> alguma coisa. Como pode uma prova transferir convicção sem transferir informação nenhuma?</p>
<p>A matemática das Peças 2 a 5 é a resposta completa, mas a intuição demora a assentar. Então aqui vão duas analogias clássicas que não envolvem equação nenhuma — cada uma ilumina uma metade do conceito.</p>
<p><strong>Onde está o Wally?</strong> Eu digo que encontrei o Wally na página e quero te provar isso sem revelar onde ele está — senão você ganha o jogo de graça. A solução: pego uma folha de papelão enorme, bem maior que o livro, faço um furo pequeno no meio e posiciono sobre a página de modo que só o Wally apareça pelo furo. Você vê o Wally. Mas como o papelão cobre tudo em volta, você não faz ideia de em que canto da página ele está. Eu provei que sei a localização; você aprendeu zero sobre ela. E repare no que sustenta a prova: se eu não soubesse onde ele está, não teria como posicionar o papelão — a chance de acertar o furo no Wally por sorte é minúscula. É a caverna de Ali Babá de novo, só que com papelão.</p>
<p><strong>As duas bolas do amigo daltônico.</strong> Seu amigo é daltônico vermelho-verde. Você tem duas bolas idênticas em tamanho, peso e textura, uma vermelha e uma verde, e afirma: &ldquo;essas bolas são de cores diferentes&rdquo;. Pra ele, são indistinguíveis. Como provar sem revelar qual é qual? Ele mostra as duas bolas, uma em cada mão. Esconde as mãos atrás das costas e decide, sozinho, se troca as bolas de mão ou não. Mostra de novo e pergunta: &ldquo;troquei?&rdquo;. Você, que enxerga as cores, acerta sempre. Ele, que não enxerga, não passaria de 50%. Depois de 20 acertos seguidos, ele está convencido de que as bolas são diferentes — a chance de você blefar 20 rodadas é uma em um milhão. E o que ele aprendeu sobre qual bola é a vermelha? <strong>Nada.</strong> Cada resposta sua (&ldquo;trocou&rdquo;, &ldquo;não trocou&rdquo;) era algo que ele mesmo já sabia — afinal, foi ele quem decidiu se trocava ou não. A informação da rodada já era dele; a única coisa nova é a convicção de que você enxerga a diferença.</p>
<p>A versão formal dessa intuição é o simulador da Peça 5: se qualquer pessoa, sem saber segredo nenhum, consegue fabricar a &ldquo;gravação&rdquo; de uma sessão de prova indistinguível de uma sessão real, então a sessão real não pode carregar informação sobre o segredo. O poder de convencer vem da ordem das operações — primeiro o compromisso, depois o desafio. O conhecimento zero vem do fato de que a gravação, sozinha, é falsificável. As duas coisas são verdade ao mesmo tempo, por razões diferentes. É por isso que o conceito escorrega: você segura dois fatos que parecem se contradizer até perceber que cada um se apoia numa coisa distinta.</p>
<p>E se mesmo depois de tudo isso o conceito continua escorregadio, ótimo: você acabou de sentir na pele o argumento central deste artigo.</p>
]]></content:encoded><category>politica</category><category>seguranca</category><category>tutoriais</category></item><item><title>Omarchy 2.0 - Básico de LazyVim</title><link>https://akitaonrails.github.io/2025/09/12/omarchy-2-0-basico-de-lazyvim/</link><guid isPermaLink="true">https://akitaonrails.github.io/2025/09/12/omarchy-2-0-basico-de-lazyvim/</guid><pubDate>Fri, 12 Sep 2025 15:00:00 -0300</pubDate><description>&lt;p&gt;Droga, eu falei que o post anterior seria o último da série de &lt;a href="https://akitaonrails.github.io/tags/omarchy/"&gt;Omarchy&lt;/a&gt;, mas achei que a oportunidade é boa pra evangelizar um pouco de Vim também. Já deixei a recomendação pra seguir o canal do &lt;a href="https://www.youtube.com/@EustaquioRangel"target="_blank" rel="noopener"&gt;@Taq&lt;/a&gt;, onde ele explica muito mais a fundo como o NeoVim funciona. Também existe o &lt;a href="https://github.com/ThePrimeagen/vim-be-good"target="_blank" rel="noopener"&gt;tutorial &amp;ldquo;vim-be-good&amp;rdquo; do Primeagen&lt;/a&gt;. Esse segundo, em particular, é um tutorial interativo dentro do próprio NeoVim, pra praticar a navegação - que muitos podem achar difícil no começo.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Droga, eu falei que o post anterior seria o último da série de <a href="/tags/omarchy/">Omarchy</a>, mas achei que a oportunidade é boa pra evangelizar um pouco de Vim também. Já deixei a recomendação pra seguir o canal do <a href="https://www.youtube.com/@EustaquioRangel"target="_blank" rel="noopener">@Taq</a>, onde ele explica muito mais a fundo como o NeoVim funciona. Também existe o <a href="https://github.com/ThePrimeagen/vim-be-good"target="_blank" rel="noopener">tutorial &ldquo;vim-be-good&rdquo; do Primeagen</a>. Esse segundo, em particular, é um tutorial interativo dentro do próprio NeoVim, pra praticar a navegação - que muitos podem achar difícil no começo.</p>
<p>Vou fazer somente este um artigo sobre isso e não pretendo cobrir mais do que a ponta do Iceberg. A idéia é servir pra quem está vindo de outros editores como Sublime Text ou VS Code e não consegue começar a navegar ou até mesmo quitar do Vim kkk 🤣.</p>
<h3>Comandos Iniciais<span class="hx:absolute hx:-mt-20" id="comandos-iniciais"></span>
    <a href="#comandos-iniciais" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Como falei no <a href="https://akitaonrails.com/2025/09/07/omarchy-2-0-lazyvim-lazyextras/"target="_blank" rel="noopener">post anterior sobre LazyVim</a>, vou assumir que já entendeu pelo menos como instalar suporte à sua linguagem com <strong>LazyExtras</strong> e já aprendeu a versionar usando <a href="https://akitaonrails.com/2025/09/07/omarchy-2-0-mise-pra-organizar-ambientes-de-desenvolvimento/"target="_blank" rel="noopener"><strong>Mise</strong></a>.</p>
<p>Sem nem entrar no LazyVim, a primeira coisa a saber é que ele instala todas as configurações relevantes no diretório <code>~/.config/nvim</code>. Normalmente não precisa mexer nada lá. Deixa arquivos como <code>lazyvim.json</code> sem mexer. Notem que ele faz a mesma coisa quem um NPM da vida: tem um arquivo <code>lazy-lock.json</code> que declara a versão/commit de cada plugin instalado. O Lazyvim faz algo parecido com um <code>npm install</code> da vida. Só curiosidade pra você saber como ele se organiza.</p>
<p>Se precisar customizar algo, normalmente vai ser no diretório <code>~/.config/nvim/lua/config</code>. Foi lá onde eu adicionei suporte a Mise, por exemplo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">~/.config/nvim
</span></span><span class="line"><span class="cl">❯ cat lua/config/mise.lua
</span></span><span class="line"><span class="cl">-- Prepend mise shims to PATH
</span></span><span class="line"><span class="cl">vim.env.PATH <span class="o">=</span> vim.env.HOME .. <span class="s2">&#34;/.local/share/mise/shims:&#34;</span> .. vim.env.PATH</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Vão ser sempre scripts escritos em Lua. E nem precisa saber Lua a fundo. De bater o olho é uma linguagem muito simples, não muito diferente de um Python ou Javascript e qualquer programador deve ser capaz de ler qualquer trecho sem nenhum problema. Além disso, na maioria dos casos você vai copiar configurações que tirou da documentação, wiki ou chatgpt mesmo, então é só saber que as coisas vão nesse local.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912110425_screenshot-2025-09-12_11-04-07.png" alt="primeiros passos"  loading="lazy" /></p>
<p>Entenda que NeoVim tem o conceito de tecla &ldquo;<strong>Leader</strong>&rdquo;, é uma tecla que precede hotkeys pra outros comandos (também tem <strong>localleader</strong> que é barra invertida <strong>\</strong>). Isso é configurável mas no LazyVim o Leader é a tecla <strong>&ldquo;Espaço/Space&rdquo;</strong>. Em muita documentação vamos ver falando <em>&ldquo;apertem Leader-w&rdquo;</em>, que significa <strong>Espaço+w</strong> e agora você sabe o que isso significa.</p>
<p>Pra abrir um arquivo tem pelo menos 3 formas diferentes, 2 delas mostradas na foto acima:</p>
<blockquote>
  <p><strong>Space+E</strong> abre o <strong>NeoTree</strong> que é esse sidebar na esquerda que se parece com o Windows Explorer. Dá pra navegar com teclado ou com mouse mesmo. Ache seu arquivo e pressione &ldquo;Enter&rdquo; pra abrir, daí &ldquo;Space+E&rdquo; de novo pra esconder o sidebar. Se quiser saber como criar um novo arquivo, um novo sub-diretório, renomear e tals, digite <strong>?</strong> (tecla de interrogação) pra ver esta ajuda:</p>

</blockquote>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912142438_neotree.gif" alt="NeoTree"  loading="lazy" /></p>
<p>A terceira forma é manualmente com o comando <strong>:e</strong> seguida do nome do arquivo no diretório local. Tem auto-complete também, esse é o jeito mais rápido, se souber o que está procurando.</p>
<p>Sempre que quiser cancelar qualquer coisa ou fechar &ldquo;floating windows&rdquo; (pop-ups, dialog boxes). Só apertar &ldquo;Esc&rdquo;. Esc é fundamental no NeoVim.</p>
<p>Aberto um arquivo, ele fica no <strong>Buffer</strong>. Pense em Buffer como &ldquo;Abas Abertas&rdquo; no seu VS Code. Pra navegar por Buffer (tipo alt+tab entre seus arquivos já abertos) é simples:</p>
<ul>
<li><strong>]+b</strong> pra próxima &ldquo;aba&rdquo; (próximo Buffer)</li>
<li><strong>[+b</strong> Buffer anterior</li>
<li><strong>Space+bd</strong> pra &ldquo;fechar a aba&rdquo; (fechar o Buffer)</li>
<li><strong>Space+bo</strong> pra ficar onde está e fechar os outros buffers</li>
</ul>
<p>Abra quantos arquivos quiser. Se um deles for um daqueles logs enormes que abriu por acidente, <code>Space+bd</code> pra fechar e pronto.</p>
<p>Falando em fechar, <strong>como que sai do NeoVim?</strong> Tem vários jeitos:</p>
<ul>
<li><strong>Space+q</strong></li>
<li><strong>:q</strong></li>
<li><strong>:q!</strong></li>
</ul>
<p>A diferença: o primeiro é o jeito customizado no LazyVim. O Segundo é o jeito padrão usando o recurso de &ldquo;comandos&rdquo; que começa sempre com &ldquo;dois pontos&rdquo;. E o terceiro é se você modificou algo no arquivo mas cancelar, sair sem salvar nada, por isso a exclamação.</p>
<p>Normalmente eu uso <strong>:wq</strong> que significa &ldquo;write&rdquo; e &ldquo;quit&rdquo;. Então ele primeiro salva as mudanças no arquivo e depois já quita do programa. É útil quando estou só editando um arquivo de configuração rápido.</p>
<h3>Navegação Básica<span class="hx:absolute hx:-mt-20" id="navegação-básica"></span>
    <a href="#navega%c3%a7%c3%a3o-b%c3%a1sica" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Agora tenho um arquivo aberto. Como navegar por ele? Primeiro, como ir até o fim do arquivo e depois voltar pro começo do arquivo? É uma coisa que uso bastante, só digitar <strong>Shift+g</strong> pra ir pro fim do arquivo e depois <strong>gg</strong> pra voltar pro começo. Simples assim.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912143147_home-end.gif" alt="home-end"  loading="lazy" /></p>
<p>Agora vamos aprender vários comandos de uma só vez:</p>
<ul>
<li>Digite <strong>v</strong>. Isso inicia o <strong>&ldquo;modo de visualização&rdquo;</strong> que, na prática, significa começar a selecionar a partir de onde se encontra seu cursor.</li>
<li>Digite <strong>j</strong>. Que é o equivalente à &ldquo;seta pra baixo&rdquo; pra ir pra linha de baixo. Já deve ter ouvido falar que em Vim se navega com &ldquo;hjkl&rdquo; que são, respectivamente, pra esquerda, pra baixo, pra cima, pra direita.</li>
<li>Digite <strong>x</strong>. Que é o equivalente a &ldquo;cortar&rdquo; e colocar o conteúdo no clipboard. Feito isso, digitando <strong>p</strong> você dá &ldquo;paste&rdquo;, que é colar.</li>
<li>Digite <strong>u</strong>. Que é o comando que mais se usa: o de <strong>Undo</strong> ou desfazer a última modificação.</li>
</ul>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912143741_marcar-apagar.gif" alt="marcar-apagar"  loading="lazy" /></p>
<p>Isso tudo funciona no chamado <strong>&ldquo;modo de comando&rdquo;</strong>, que é o modo padrão que o Vim abre todo arquivo. Nesse modo não dá pra digitar nenhum texto. Como podem ver, cada letra sozinha é um comando, como &ldquo;x&rdquo; pra cut ou &ldquo;p&rdquo; pra paste.</p>
<p>Pra digitar, primeiro precisa digitar <strong>i</strong> (só a letra &ldquo;i&rdquo; em minúsculo), que daí muda do modo de comando pro <strong>&ldquo;modo de edição&rdquo;</strong>. Agora é só digitar seu texto normalmente, como em qualquer editor. Quando quiser algum comando, basta digitar <strong>ESC</strong> pra voltar pro modo de comando. Vamos fazer isso o tempo todo, por isso muita gente re-mapeia a tecla &ldquo;caps lock&rdquo; do teclado pra ser a ESC e ficar fácil só tocar com o mindinho esquerdo.</p>
<h3>Combinando Comandos<span class="hx:absolute hx:-mt-20" id="combinando-comandos"></span>
    <a href="#combinando-comandos" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><blockquote>
  <p>Ainda no modo de comando, como navegamos pelo arquivo mais fácil?</p>

</blockquote>
<p>Além das teclas &ldquo;hjkl&rdquo;, podemos navegar mais fácil usando <strong>b</strong> pra &ldquo;palavra anterior&rdquo; ou <strong>w</strong> pra palavra (<em>word</em>) posterior:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912144226_words.gif" alt="words"  loading="lazy" /></p>
<p>O modo de comando permite combinar comandos, portanto podemos fazer:</p>
<ul>
<li><strong>dw</strong> pra apagar a próxima palavra</li>
<li><strong>db</strong> pra apagar a palavra anterior</li>
<li><strong>d4w</strong> pra apagar (delete) as 4 palavras (words) seguintes</li>
<li><strong>d4j</strong> pra apagar as 4 linhas pra baixo</li>
</ul>
<p>O <a href="https://github.com/ThePrimeagen/vim-be-good"target="_blank" rel="noopener">tutorial do Primeagen</a> tem vários exercícios pra esse tipo de navegação, recomendo se exercitar. Num VS Code todo mundo está acostumado a primeiro pegar o mouse, marcar o bloco que quer apagar visualmente, depois apertar delete ou backspace. No Vim, olhamos onde está o cursor, apertamos ESC pra estar no modo de comando, e digitamos a operação e a localização. Teste todas as combinações que conseguir imaginar.</p>
<p>Esta é a <strong>mudança de paradigma</strong> que incomoda os preguiçosos. Mas uma vez aprendido isso, você vai achar incômodo um editor que te obrigado a usar mouse pra coisas básicas.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912144801_change.gif" alt="change in place"  loading="lazy" /></p>
<p>Agora fica mais interessante: <em>e se em vez de apagar uma palavra, eu queira substituir a palavra onde meu cursor está pra outra palavra?</em></p>
<p>Num editor normal, você marcaria com o mouse (ou usando alguma combinação como &ldquo;ctrl+shift+seta&rdquo;), e digitaria por cima ou apagaria a palavra.</p>
<p>No LazyVim tem plugins configurados pra coisas mais interessantes. No video acima veja como estou substituindo palavras <em>&ldquo;in-place&rdquo;</em>. Isso se faz com o comando <strong>ciw</strong>.</p>
<ul>
<li><strong>ciw</strong> apaga a palavra inteira embaixo do cursor e abre o modo de edição pra você digitar a substituição</li>
<li><strong>ce</strong> apaga a partir do cursor até o fim da palavra (preserva o começo da palavra) e também abre pra você digitar no lugar</li>
<li><strong>Shift-c</strong> (C maiúsculo) apaga tudo a partir do seu cursor até o fim da linha inteira e abre pra você digitar.</li>
</ul>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912145439_bloco.gif" alt="Mudar Bloco Vertical"  loading="lazy" /></p>
<p>Mas isso ainda não parece tãaaaao legal. E se quiser mudar só a primeira letra de várias linhas ao mesmo tempo, por exemplo, no texto do video acima, quero mudar só o hífen (&quot;-&quot;) de cada linha de tags?</p>
<p>Falei antes que apertando <strong>v</strong> no modo de comando ele começa o modo de visualização e dá pra selecionar a partir dali. Mas existe <strong>Ctrl+v</strong> que abre o modo de marcação de blocos verticais.</p>
<p>Nesse modo usamos as teclas &ldquo;hjkl&rdquo; normalmente, como <strong>j</strong> pra ir pra baixo, mas em vez de marcar a próxima linha inteira, ele marca só o caracter logo abaixo do seu cursor. Agora posso apagar com <strong>d</strong> e digitar <strong>Shift-i</strong> (&ldquo;I&rdquo; maiúsculo) pra entrar em modo de edição no começo do bloco e substituir pelo que quiser.</p>
<p>LazyVim tem plugins pra comentários, mas essa é outra alternativa se quiser adicionar &ldquo;//&rdquo; ou &ldquo;#&rdquo; no começo de várias linhas pra comentar, por exemplo. No caso, leia sobre o plugin <a href="https://github.com/folke/ts-comments.nvim"target="_blank" rel="noopener"><strong>ts-comments</strong></a> pra opções melhores pra comentário. Pra iniciar um novo bloco de comentário, digite <strong>gco</strong> pra adicionar um comentário abaixo.</p>
<p><em>&ldquo;Modo de bloco&rdquo;</em> como mostrei acima, é uma das coisas que eu mais uso em código.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912150112_search-replace.gif" alt="Search and Replace"  loading="lazy" /></p>
<p>Mas só isso é muito pouco. E se quiser substituir <strong>TODAS AS PALAVRAS</strong> iguais do texto de uma só vez. No video de exemplo acima, quero substituir &ldquo;domínio&rdquo; por &ldquo;DOMAIN&rdquo; e &ldquo;Cloudflare&rdquo; por &ldquo;AWS Amazon&rdquo;, como faço? Isso é &ldquo;Search and Replace&rdquo; que tem em todo editor e em Vim é muito fácil:</p>
<p>Digitamos <strong>:%s/domínio/DOMAIN/</strong>.</p>
<p>Se você é programador já deve ter visto essa sintaxe antes. É a sintaxe de <strong>REGEX MATCH</strong>. Sim, tem que saber Regex (e não, não é nem PCRE e nem Unicode, é um dialeto de Regex do próprio NeoVim, mas é parecido).</p>
<p>Como falei antes, &ldquo;dois pontos&rdquo; abre o box de digitar comandos, <strong>&quot;%s&quot;</strong> inicia &ldquo;search com regex&rdquo; e entre as barras &ldquo;//&rdquo; primeiro vai o que procura e depois com o que quer substituir.</p>
<p>Mas esse é o jeito no NeoVim puro, no LazyVim, tem o plugin <a href=""><strong>grug-far</strong></a> e se ativa com o comando <strong>SPACE+sr</strong>:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912150648_screenshot-2025-09-12_15-06-32.png" alt="grug-far"  loading="lazy" /></p>
<p>Agora sim, mais parecido com do VS Code, né? Use o que achar mais fácil.</p>
<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Como falei, isto é somente a ponta da ponta do iceberg, o mínimo do mínimo suficiente pra pelo menos começar editando arquivos simples de configuração sem abrir Nano ou VS Code só pra isso.</p>
<p>Leia a documentação do LazyVim. Veja os <a href="https://www.lazyvim.org/keymaps"target="_blank" rel="noopener">keymaps</a> pra descobrir outros comandos. Preste atenção em cada letra que aperta no modo de comando, o LazyVim mostra as opções disponíveis. Por exemplo, digamos que eu apertei <strong>g</strong> (goto), ele vai mostrar uma ajuda no canto inferior direito:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912151849_screenshot-2025-09-12_15-18-38.png" alt="ajuda"  loading="lazy" /></p>
<p>Assim você sempre pode descobrir novos comandos ou relembrar comandos que usa pouco. Por isso recomendo o LazyVim, que já vem bem configurado pra te ajudar. Quem faz essa funcionalidade é o plugin <a href="https://github.com/folke/which-key.nvim"target="_blank" rel="noopener"><strong>which-key</strong></a>, caso queira a mesma coisa num NeoVim que você configurou do zero.</p>
<p>Existem diversos <a href="https://vim.rtorr.com/"target="_blank" rel="noopener"><strong>Cheatsheets</strong></a> por aí com tabelas de combinações de comandos importantes. Vale explorar.</p>
<p>Mas não é só isso, LazyVim vem com dezenas de <a href="https://www.lazyvim.org/plugins"target="_blank" rel="noopener">plugins</a> pra formatação, linting, LSP integrado, GIT integrado, e muito mais. Não tem como eu documentar um a um aqui. Cada um deles tem sua própria documentação na página de plugins do LazyVim.org.</p>
<p>Novamente, não esqueça de chamar o <strong>:LazyExtras</strong> e instalar os plugins opcionais das suas linguagens.</p>
]]></content:encoded><category>omarchy</category><category>ferramentas-de-desenvolvimento</category><category>tutoriais</category></item><item><title>Omarchy 2.0 - Indicado pra Iniciantes?</title><link>https://akitaonrails.github.io/2025/09/12/omarchy-2-0-indicado-pra-iniciantes/</link><guid isPermaLink="true">https://akitaonrails.github.io/2025/09/12/omarchy-2-0-indicado-pra-iniciantes/</guid><pubDate>Fri, 12 Sep 2025 12:00:00 -0300</pubDate><description>&lt;p&gt;O video mais importante do meu &lt;a href="https://www.youtube.com/@Akitando"target="_blank" rel="noopener"&gt;canal de YouTube&lt;/a&gt; é o &amp;ldquo;Não Terceirize suas Decisões&amp;rdquo;:&lt;/p&gt;
&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/D3L8IOncLkg"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;As perguntas que eu mais recebo, literalmente todos os dias, são no formato: &lt;em&gt;&amp;ldquo;Akita, devo fazer faculdade?&lt;/em&gt;, &lt;em&gt;&amp;ldquo;Akita, o que você acha que eu devo estudar?&amp;rdquo;&lt;/em&gt; e variações dessas.&lt;/p&gt;
&lt;p&gt;Velho, eu não te conheço, nunca vou saber nem me interessar em saber quem você é, o que almeja, quais suas aspirações, ambições, gostos, preferência. Nada. Nem eu, nem &lt;strong&gt;NINGUÉM&lt;/strong&gt;. Só você sabe o que é melhor pra você. Se não sabe, trate de procurar saber. É isso que &lt;strong&gt;VIVER&lt;/strong&gt; significa.&lt;/p&gt;</description><content:encoded><![CDATA[<p>O video mais importante do meu <a href="https://www.youtube.com/@Akitando"target="_blank" rel="noopener">canal de YouTube</a> é o &ldquo;Não Terceirize suas Decisões&rdquo;:</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/D3L8IOncLkg"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>As perguntas que eu mais recebo, literalmente todos os dias, são no formato: <em>&ldquo;Akita, devo fazer faculdade?</em>, <em>&ldquo;Akita, o que você acha que eu devo estudar?&rdquo;</em> e variações dessas.</p>
<p>Velho, eu não te conheço, nunca vou saber nem me interessar em saber quem você é, o que almeja, quais suas aspirações, ambições, gostos, preferência. Nada. Nem eu, nem <strong>NINGUÉM</strong>. Só você sabe o que é melhor pra você. Se não sabe, trate de procurar saber. É isso que <strong>VIVER</strong> significa.</p>
<p>O que você não admite é que quer que alguém que considera mais importante ou relevante aprovando sua escolha e dizendo que está certo.</p>
<p>Nunca espere isso de mim, eu nunca vou te validar.</p>
<p>Sempre vou dizer: <strong>SE VIRA</strong>. Foi assim que eu me tornei quem eu sou. Eu nunca segui ninguém, nunca perguntei pra ninguém o que deveria fazer. Simplesmente ia lá e fazia. Quebrava a cara, caía, levantava, e tentava de novo. É a vida.</p>
<p>Deixa eu mostrar o que me <strong>INCOMODA</strong> mesmo:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912124608_screenshot-2025-09-12_12-45-47.png" alt="pergunta idiota"  loading="lazy" /></p>
<p>Vamos ver no <a href="https://learn.omacom.io/2/the-omarchy-manual/93/security"target="_blank" rel="noopener">manual</a>?</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912124657_screenshot-2025-09-12_12-46-45.png" alt="Manual"  loading="lazy" /></p>
<p>O DHH se deu ao trabalho de escrever um manual. Tem uma seção chamada &ldquo;Security&rdquo;. Se tivesse ido no Google, teria achado. Ou esta outra pérola:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912124836_screenshot-2025-09-12_12-48-24.png" alt="devops"  loading="lazy" /></p>
<p>Como quer ser &ldquo;DEVOPS&rdquo; (que tem que saber TUDO de Linux) começando assim. Já começou errado. Senta a bunda na cadeira, arregaça as mangas, e testa um por um, e aprende.</p>
<h3>Linux pra Iniciante?<span class="hx:absolute hx:-mt-20" id="linux-pra-iniciante"></span>
    <a href="#linux-pra-iniciante" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Isso dito, assista meu video. Mas como tenho falado bastante de <a href="/tags/omarchy/">Omarchy</a> já digo que sim, este é o que eu uso no meu dia a dia e é único que vou recomendar no futuro próximo (depois posso mudar de idéia). Tendo usado distros desde que Linux foi inventado no começo dos anos 90, eu sei que existem dezenas de distros e nem eu tenho paciência pra experimentar todos.</p>
<p><em>Por que Omarchy?</em></p>
<p>Primeiro porque ele fez a escolha certa da distro base: <strong>Arch Linux</strong>. Considero que é a melhor distro que existe hoje por dois fatores: ele tem pouco &ldquo;bloat&rdquo; (não instala um monte de coisas que você nunca vai usar, como um Ubuntu, Fedora, Mint, Deepin ou qualquer outros desses &ldquo;pra iniciantes&rdquo;).</p>
<p>Segundo, Arch Linux tem o AUR, que é um repositório de pacotes feitos pro usuários que tem basicamente tudo. Não precisa ficar caçando repositórios de terceiros ou ter que instalar manualmente. Com um único comando, o <code>yay</code>, instala-se tudo. Não existe isso de <code>apt-repository add</code> ou ter que fazer <code>make install</code> nem nada disso. O AUR gerencia tudo pra você.</p>
<p>Terceiro, ele instala Hyprland (que eu já usava e gostava), com configurações muito boas, escolha de pacotes muito bons. Tudo que eu mesmo instalaria manualmente. Então é uma opção mil vezes mais fácil pra ter o que eu teria muito mais trabalho pra fazer sozinho. Veja minha <a href="/tags/omarchy/">série de posts sobre Omarchy</a> pra ver essas opções.</p>
<h3>Linux pra Iniciantes?<span class="hx:absolute hx:-mt-20" id="linux-pra-iniciantes"></span>
    <a href="#linux-pra-iniciantes" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><em>Iniciante em que?</em></p>
<p>Eu odeio essa pergunta. Se você é um usuário amador mesmo, não programador, talvez seja um contador, talvez seja só um adolescente gamer. A primeira pergunta que faço é: <em>pra que você quer usar Linux??</em></p>
<p>Digamos que seja porque ouviu falar que é mais seguro (de fato é). Então tanto faz, qualquer distro serve. Instale Ubuntu, instale Manjaro, instale Garuda. Não faz diferença, porque você nunca vai lidar com nada por baixo dos panos. Vai abrir o Chrome, vai abrir o Steam, e é só isso. O resto vai ser web-apps.</p>
<p>Digamos que é só pra jogar, então instale <a href="https://bazzite.gg/"target="_blank" rel="noopener">Bazzite</a> no seu mini-PC ou handheld. Ou só instale seus jogos num Windows separado só pra jogos como eu fiz. Tanto faz.</p>
<p>Ou fique no Windows ou MacOS mesmo. Se for totalmente amador, provavelmente vai usar a mesma senha fraca pra tudo, vai sair clicando em links do Whatsapp sem ver de quem é. Literalmente tanto faz qual OS vai usar. Nenhuma segurança de nenhum OS vai proteger seus maus hábitos e ignorância.</p>
<p>Se for iniciante em programação ou tecnologia, aí eu não tenho dó: aprenda Linux direito. Você quer ser um profissional, seja um profissional, aprenda do jeito certo: aprenda a ter 100% de controle sob sua própria máquina. Não tem passo a passo nem tutorial pra isso. É algo que você vai fazer continuamente pro resto da vida. Eu tenho mais de 30 anos na área e continuo aprendendo coisas novas todos os dias.</p>
<h3>Hardware<span class="hx:absolute hx:-mt-20" id="hardware"></span>
    <a href="#hardware" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O <strong>MAIOR</strong> problema que qualquer pessoa vai ter, com QUALQUER distro vai ser suporte a hardware. E eis porque você não deve só seguir o que alguém te recomenda:</p>
<p><strong>Ninguém tem o mesmo PC que você.</strong></p>
<p>Qual é seu notebook? Um Lenovo? Um Dell? Um Positivo? De que ano? Qual modelo?</p>
<p>Tudo vai variar: qual é o dispositivo de Wi-fi e bluetooth? qual é a placa gráfica? Qual firmware está rodando? Com que suportes de perfis de energia? Tem necessidade de dispositivos FIDO2 pra coisas como leitor de impressão digital? Hardware keys? Tem TPU na sua máquina? Vai precisar de Secure Boot?</p>
<p>Não sei. Você que tem que saber.</p>
<p>Um exemplo prático, eu tenho um notebook <strong>Asus Zephyrus G14</strong> que uso com Windows porque serve só na minha oficina pra rodar Bambu Studio pra controlar minha Impressora 3D X1C. Ainda não parei pra instalar Omarchy nele, mas digamos que eu queira. Vai funcionar?</p>
<p>Felizmente existe <a href="https://wiki.archlinux.org/title/ASUS_ROG_Zephyrus_G16_%282023%29_GU603"target="_blank" rel="noopener">uma página no ArchWiki</a> dedicada a este modelo. Ao que parece, funciona tudo &ldquo;out-of-the-box&rdquo;. Mas se fosse a <a href="https://wiki.archlinux.org/title/ASUS_ROG_Zephyrus_G14_%282022%29_GA402"target="_blank" rel="noopener">versão do ano anterior</a> já teria que checar mais ítens extras. Tem problemas no Wi-fi, por exemplo.</p>
<p>Cada máquina diferente vai ter problemas diferentes.</p>
<h3>Live Env e Ventoy<span class="hx:absolute hx:-mt-20" id="live-env-e-ventoy"></span>
    <a href="#live-env-e-ventoy" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><em>&ldquo;O que eu faço então, me ph0di?</em></p>
<p>É por isso que praticamente toda boa distro oferece a ISO de instalação que vem com um ambiente vivo (Live Environment). Já vi que tem muita gente no Windows que tem dificuldade do que fazer com uma ISO. É muito simples.</p>
<p><img src="https://etcher.balena.io/images/Etcher_steps.gif" alt="Balena Etcher"  loading="lazy" /></p>
<p>O programa mais fácil de usar é o <a href="https://etcher.balena.io/"target="_blank" rel="noopener">Balena Etcher</a> Você baixa o arquivo com extensão <code>.iso</code>, seja do Ubuntu, Mint, Elementary, etc (SEMPRE DO SITE OFICIAL!!!!). Abre o Balena e só tem 2 passos: selecione a imagem (o arquivo ISO), selecione o drive (seu pen drive, cuidado pra não escolher seu HD!), e pronto.</p>
<p>Agora é só bootar seu PC e entrar na BIOS pra selecionar o pen drive pra bootar ou usar uma tecla como F12 (varia de BIOS pra BIOS) pro UEFI selecionar seu pen drive.</p>
<p>Isso vai bootar o Live Environment direto do pen drive.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912120806_try-ubuntu-install-ubuntu-800x530.jpeg" alt="Try Ubuntu"  loading="lazy" /></p>
<p>A maioria das distros começa abrindo uma tela de &ldquo;Bem Vindo&rdquo; parecida com a de cima. Esse aplicativo de instalação se chama <a href="https://calamares.io/"target="_blank" rel="noopener"><strong>Calamares</strong></a> e todo mundo com instalação gráfica usa ele. Por isso é parecido em todos. É o mesmo passo a passo. Se instalou uma distro já instalou todas.</p>
<p>Só que você não precisa seguir o Calamares. Pode fechar ele. E agora está já no ambiente gráfico da distro que escolheu pra instalar. <strong>USE ELE.</strong> Tente configurar seu Wi-fi, tente parear seu fone ou mouse Bluetooth, tente ligar a WebCam, coloque um micro-SD card e veja se ele aparece. Tente navegar na web e sinta a performance.</p>
<p>Se nesse ponto conseguir fazer todas as coisas básicas, significa que as chances de tudo funcionar é grande. Se já neste ponto começar a ter problemas, como não conseguir conectar no seu Wi-fi, possivelmente essa distro vai te dar dor de cabeça. Comece indo no Google e veja se alguém já solucionou o problema pro seu modelo de notebook ou PC, <strong>antes</strong> de continuar com o Calamares.</p>
<p>Digamos que você decidiu tirar o dia pra testar várias distros pra ver qual roda melhor na sua máquina. Ótima decisão, mas já deve ter entendido que vai ser um enorme saco ter vários pen drives, um pra cada distro. Ou toda hora ter que voltar pro Windows e usar o Balena pra sobrescrever uma nova distro no seu único pendrive.</p>
<p>Não precisa fazer isso.</p>
<p>Basta baixar e instalar primeiro o <a href="https://www.ventoy.net/en/index.html"target="_blank" rel="noopener"><strong>Ventoy</strong></a> no seu pendrive. Veja a <a href="https://www.ventoy.net/en/doc_start.html"target="_blank" rel="noopener">documentação</a> no site deles, mas é ridiculamente simples.</p>
<p><img src="https://www.ventoy.net/static/img/screen/screen_uefi_en.png?v=4" alt="Ventoy Menu"  loading="lazy" /></p>
<p>Depois de instalar no seu pendrive, agora é só copiar <strong>TODAS AS ISOS</strong> que quiser de uma só vez no pendrive. Quando bootar por ele, vai entrar no menu do Ventoy, que vai deixar escolher qualquer uma das ISOs que copiou lá. Se não gostou da distro atual, só rebootar no Ventoy de novo, e escolher outra ISO. É assim simples.</p>
<p><img src="https://www.ventoy.net/static/img/ventoy2disk_en.png" alt="Ventoy"  loading="lazy" /></p>
<h3>Distrobox<span class="hx:absolute hx:-mt-20" id="distrobox"></span>
    <a href="#distrobox" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Digamos que você é um programador, ou um devops, ou qualquer profissional na área de tecnologia. Sua preocupação não é tanto a interface gráfica (que dá pra testar com Ventoy), mas sim se a infra do OS vai funcionar com seus requerimentos de desenvolvimento.</p>
<p>Digamos que você use Fedora. Será que se eu for pro Mint, vai ter os pacotes pro K8S do jeito que eu preciso? Será que tem suporte à versão de Java que eu uso? Será que se eu buildar um pacote <code>.deb</code> no meu Fedora, vai instalar direitinho no Ubuntu?</p>
<p>Ter que fazer dual boot ou instalar uma VM parece pesado demais, trabalhoso demais, só pra testar 2 linhas de comando, por exemplo.</p>
<p><img src="https://user-images.githubusercontent.com/598882/144294862-f6684334-ccf4-4e5e-85f8-1d66210a0fff.png" alt="Distrobox"  loading="lazy" /></p>
<p>Pra isso existe <a href="https://distrobox.it/"target="_blank" rel="noopener"><strong>Distrobox</strong></a>. Ele precisa do Docker ou Podman já instalado na sua máquina. Mas depois é só fazer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">distrobox create -n debian-test --init --image debian:latest --additional-packages <span class="s2">&#34;build-essential&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Uma vez criado o box é só entrar nele:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">distrobox enter debian-test</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Acabou, você está dentro do Debian, super rápido. Como é um container docker, tem como mapear diretórios na sua máquina local pra dentro do container, então fica tudo integrado. E o mais importante: carrega super rápido, porque é só um container Docker.</p>
<p>O guia <a href="https://distrobox.it/#quick-start"target="_blank" rel="noopener">Quick Start</a> explica tudo isso e tem este video com um tour pra demonstrar como funciona:</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/Q2PrISAOtbY"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Sério. Com Distrobox não tem desculpa pra não testar outra distro em 2 minutos. Não fique perdendo tempo mandando DM pra &ldquo;influencer&rdquo;. Veja você mesmo! Se não é capaz de fazer isso, talvez esteja na profissão errada.</p>
<p>Todo &ldquo;dev&rdquo; ou profissional de tecnologia, seja de devops, cybersec, data analyst, bla bla bla, seja lá qual título da moda hoje em dia, <strong>DEVE</strong> ter controle sobre sua própria máquina. <strong>DEVE</strong> saber lidar com segurança (por isso fiz posts sobre Vaultwarden, Cloudflare, SSH, etc).</p>
<p><strong>É SUA RESPONSABILIDADE!</strong></p>
<p>Não terceirize sua responsabilidade pros outros. Não procure validação. Seja <strong>promíscuo</strong> e teste quais distros quiser, e faça o que quiser nessas distros: é sua! Não existe &ldquo;distro pra iniciante&rdquo;, &ldquo;distro pra dev&rdquo;, &ldquo;distro pra devops&rdquo;. Distro é Distro. Linux é Linux. É tudo igual, o que varia é <strong>sua habilidade</strong>. Se não tem habilidade: conquiste! Aprenda! Quebre a cabeça! Vare noites tunando configs! Leia as documentações oficiais! Não perca tempo achando que vai aprender em rede social. Não vai.</p>
<p>Eu uso Linux desde o começo dos anos 90 e não fico usando Slackware só porque foi a primeira que aprendi e sinto necessidade de validar minha primeira escolha. Eu uso todas que quiser. Agora mesmo, eu tinha minha própria instalação de Hyprland, resolvi que quero aprender e usar Omarchy. Passei a semana quebrando cabeça, escrevi meia dúzia de posts no blog. E agora eu aprendi. Eu sei Omarchy: mais um pra coleção.</p>
]]></content:encoded><category>omarchy</category><category>linux</category><category>tutoriais</category></item><item><title>Omarchy 2.0 - Instale com a ISO do Omarchy</title><link>https://akitaonrails.github.io/2025/09/12/omarchy-2-0-instale-com-a-iso-do-omarchy/</link><guid isPermaLink="true">https://akitaonrails.github.io/2025/09/12/omarchy-2-0-instale-com-a-iso-do-omarchy/</guid><pubDate>Fri, 12 Sep 2025 10:00:00 -0300</pubDate><description>&lt;p&gt;Pra finalizar a série de &lt;a href="https://akitaonrails.github.io/tags/omarchy/"&gt;Omarchy&lt;/a&gt; por enquanto, acho que faltou comentar rapidamente sobre a instalação via a ISO do próprio Omarchy.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912093827_screenshot-2025-09-12_09-38-05.png" alt="ISO" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;Só ir no &lt;a href="https://omarchy.org/"target="_blank" rel="noopener"&gt;site oficial&lt;/a&gt; e clicar em &amp;ldquo;ISO&amp;rdquo; pra baixar. Usar um Rufus ou Balena Etcher no Windows (ou &lt;a href="https://github.com/ifd3f/caligula"target="_blank" rel="noopener"&gt;Caligula&lt;/a&gt; em Linux) pra gravar num pen drive e bootar com ele. Dali em diante é seguir o passo-a-passo e em menos de 5 minutos vai ter tudo instalado.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Pra finalizar a série de <a href="/tags/omarchy/">Omarchy</a> por enquanto, acho que faltou comentar rapidamente sobre a instalação via a ISO do próprio Omarchy.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20250912093827_screenshot-2025-09-12_09-38-05.png" alt="ISO"  loading="lazy" /></p>
<p>Só ir no <a href="https://omarchy.org/"target="_blank" rel="noopener">site oficial</a> e clicar em &ldquo;ISO&rdquo; pra baixar. Usar um Rufus ou Balena Etcher no Windows (ou <a href="https://github.com/ifd3f/caligula"target="_blank" rel="noopener">Caligula</a> em Linux) pra gravar num pen drive e bootar com ele. Dali em diante é seguir o passo-a-passo e em menos de 5 minutos vai ter tudo instalado.</p>
<p><img src="https://learn.omacom.io/u/configurator-iGH96F.png" alt="Omarchy install"  loading="lazy" /></p>
<p>Como expliquei no <a href="https://akitaonrails.com/2025/08/29/new-omarchy-2-0-install/"target="_blank" rel="noopener">primeiro artigo</a> eu fiz diferente: instalei o <a href="https://archlinux.org/download/"target="_blank" rel="noopener">Arch Linux</a> puro, tipo Desktop, com Hyprland + SDDM. Depois instalei Omarchy usando a linha de comando manual:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -fsSL https://omarchy.org/install <span class="p">|</span> bash</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Dá pra fazer dos dois jeitos, no segundo tem mais controle, mas depois de ganhar mais experiência e ler os scripts de install, eu recomendo que baixe a ISO do próprio Omarchy, vai ser mais fácil mesmo.</p>
<h3>Limine vs Grub<span class="hx:absolute hx:-mt-20" id="limine-vs-grub"></span>
    <a href="#limine-vs-grub" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O motivo de ter instalado da forma mais manual foi porque eu não tinha certeza se o Omarchy tinha suporte a dar rollback de BTRFS via snapshots. Eu tinha ouvido falar que o bootload <a href="https://codeberg.org/Limine/Limine"target="_blank" rel="noopener">Limine</a> não suportava isso. Mas me enganei, o Omarchy instala Limine com suporte a <strong>Snapper</strong> (que é similar ao Timeshift que eu costumo usar). Leia no <a href="https://wiki.archlinux.org/title/Limine"target="_blank" rel="noopener">ArchWiki seção 6.2</a>, portanto deve ser uma opção superior à minha instalação de GRUB+Timeshift que falei no primeiro artigo.</p>
<p>Como eu já instalei de forma manual, vou manter como está porque na prática é a mesma coisa. Mas se fosse instalar de novo, agora instalaria com Limine+Snapper pra experimentar.</p>
<p>Significa que existe suporte a snapshot do seu filesystem inteiro com BTRFS antes de um update de pacotes e, se algo der errado, é possível rebootar e dar rollback pra um snapshot anterior. Isso te dá segurança de não perder um dia inteiro tentando consertar alguma coisa, caso algum pacote importante do sistema venha com bugs ou corrompido. É o recurso mais importante.</p>
<h3>SDDM vs UWSM<span class="hx:absolute hx:-mt-20" id="sddm-vs-uwsm"></span>
    <a href="#sddm-vs-uwsm" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Outra coisa diferente que notei foi o login manager. No Arch puro, ele instala Hyprland por padrão com <a href="https://wiki.archlinux.org/title/SDDM"target="_blank" rel="noopener">SDDM</a>, que é o mesmo que o KDE usa.</p>
<p>Omarchy usa UWSM, que faz login automático com seu usuário no boot. Então não tem tela de login, ele já abre o Hyprland automaticamente.</p>
<p><em>&ldquo;Mas isso não é inseguro??&rdquo;</em></p>
<p>Não, porque a ISO do Omarchy pré-configura seu SSD com LUKS, antes do filesystem.</p>
<p>Pra quem não sabe, <a href="https://wiki.archlinux.org/title/Dm-crypt/Encrypting_an_entire_system"target="_blank" rel="noopener">LUKS</a> é o <strong>Linux Unified Key Setup</strong>, que dá suporte a encriptar seu disco inteiro. A vantagem dele em relação a um BitLocker do Windows, é que ele é <strong>agnóstico a filesystem</strong>. Ou seja, funciona com BTRFS ou EXT4 ou qualquer outro, porque na sequência de boot, depois do bootloader (seja Limine ou Grub), ele carrega <strong>antes</strong> do filesystem. É um encriptador em nível de blocos.</p>
<p><img src="https://learn.omacom.io/u/arch-encryption-urjrDm.png" alt="install luks"  loading="lazy" /></p>
<p>Portanto, quando você bootar seu sistema, primeiro vai pedir a senha de decriptar seu disco. Daí abre o filesystem e o kernel do Linux pode continuar o boot normal com o disco aberto. Então você é obrigado a <strong>sempre</strong> digitar a senha segura do seu usuário no boot pra decriptar o disco.</p>
<p>Portanto, ter que digitar senha novamente num login manager seria redundante e desnecessário, por isso imagino que escolheram usar UWSM em vez de SDDM.</p>
<p>Especialmente em notebook, ou em casos onde mais gente mora com você, é <strong>essencial</strong> garantir que seu disco esteja encriptado, não importa o sistema operacional. Digamos que saia pra trabalhar ou vá viajar e seu notebook é roubado. Sem encriptação, acabou, não importa se tem um login manager. Ele não protege seus dados. Basta tirar o SSD do seu notebook, plugar em outro computador e pronto, dá pra copiar tudo bootando como Root.</p>
<p>Com encriptação, sem a senha pra decriptar, é impossível acessar seus dados. Impossível mesmo, porque em qualquer computador modernos dos últimos 15 anos, a CPU vem com instruções aceleradas em hardware pra encriptar usando AES 256.</p>
<p>Sendo um pouco mais nerd, a chave de decriptação <strong>não é sua senha</strong>. Durante o processo de instalação vai ser gerada uma nova senha mais forte e segura usando um processo de <strong>derivação de chaves</strong> (KDF - Key Derivation Function), hoje em dia é com Argon2id, antigamente era com PBKDF2. Explico sobre isso neste video:</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/HCHqtpipwu4"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Portanto, quando digita sua senha, na verdade ele decripta essa chave segura, e é com essa chave segura que seu disco está encriptado. Por isso não dá pra decriptar seus dados na força-bruta.</p>
<p>Além disso AES 256 é outro algoritmo de criptografia que é <strong>quantum resistant</strong>. Mesmo se fosse possível um computador quântico (não é), nem ele conseguiria decriptar seu disco. Este é o nível de segurança.</p>
<p><em>&ldquo;Mas não vai ficar mais lento?&rdquo;</em></p>
<p>Não. É praticamente imperceptível, porque AES - como já disse - é acelerado via hardware pela sua CPU, não depende de software do seu sistema operacional. No dia a dia, dificilmente vai notar. A performance é praticamente a mesma. Não existe motivo pra não ativar encriptação, especialmente num notebook.</p>
<p>No meu caso pessoal, onde instalei num PC desktop, que eu mesmo controlo numa rede local segura - porque eu sei implementar segurança em rede, não tem problema. Os dados mais importantes pra mim ficam no meu NAS (que é encriptado), em backups (que são encriptados), eu não tenho dados sensíveis neste PC e o pouco que tenho está em arquivos encriptados pelo <a href="https://veracrypt.io/en/Downloads.html"target="_blank" rel="noopener">Veracrypt</a> num sub-volume separado.</p>
<p>Por isso não habilitei LUKS no meu PC. Daí mantive o SDDM que o Arch puro instalou. Só que ele é <strong>BEM</strong> feio por padrão, então só fica uma pequena dica: instalar ou o tema <a href="https://github.com/MarianArlt/sddm-sugar-dark"target="_blank" rel="noopener">sddm-sugar-dark</a> ou o <a href="https://github.com/simonesestito/dark-arch-sddm"target="_blank" rel="noopener">dark-arch-sddm</a>. Eu prefiro esse segundo por ser mais minimalista e fica assim:</p>
<p><img src="https://github.com/simonesestito/dark-arch-sddm/raw/master/base/screenshot.png" alt="dark arch sddm"  loading="lazy" /></p>
<p>Eu customizei e troquei o logo do Arch pelo do Omarchy e pronto.</p>
<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Vou repetir: só depois que instalei manualmente que vi como o Omarchy instala e hoje eu prefiro o jeito do Omarchy. Se fosse instalar do zero, usaria a ISO do Omarchy e deixaria com Limine+Snapper e Hyprland+UWSM com suporte a rollback de snapshot, encriptação total do disco, login automático pro Hyprland.</p>
<p>Dá pra ver que o pessoal fazendo o Omarchy tem experiência e já escolheu componentes que fazem sentido pra funcionar tudo junto da forma mais conveniente e segura possível. Eu tive receio que por ser uma distro nova talvez eles não tivessem se preocupado com isso, mas fico feliz de dizer que eu estava errado.</p>
<p>Usem a ISO oficial do Omarchy.</p>
]]></content:encoded><category>omarchy</category><category>linux</category><category>tutoriais</category></item><item><title>Omarchy 2.0 - LazyVim - LazyExtras</title><link>https://akitaonrails.github.io/2025/09/07/omarchy-2-0-lazyvim-lazyextras/</link><guid isPermaLink="true">https://akitaonrails.github.io/2025/09/07/omarchy-2-0-lazyvim-lazyextras/</guid><pubDate>Sun, 07 Sep 2025 16:00:00 -0300</pubDate><description>&lt;p&gt;Continuando meus posts de dicas sobre &lt;a href="https://akitaonrails.com/tags/omarchy/"target="_blank" rel="noopener"&gt;Omarchy&lt;/a&gt; resolvi postar sobre &lt;a href="https://www.lazyvim.org/"target="_blank" rel="noopener"&gt;LazyVim&lt;/a&gt; que já vem pré-instalado.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://user-images.githubusercontent.com/292349/213447056-92290767-ea16-430c-8727-ce994c93e9cc.png" alt="LazyVim" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;Mesmo se não usa Omarchy, recomendo &lt;a href="https://www.lazyvim.org/installation"target="_blank" rel="noopener"&gt;instalar&lt;/a&gt; LazyVim, só fazer isso:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div&gt;&lt;pre&gt;&lt;code&gt;# required
mv ~/.config/nvim{,.bak}
# optional but recommended
mv ~/.local/share/nvim{,.bak}
mv ~/.local/state/nvim{,.bak}
mv ~/.cache/nvim{,.bak}
git clone https://github.com/LazyVim/starter ~/.config/nvim
rm -rf ~/.config/nvim/.git
nvim&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Copy code"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Pronto, é só isso. NeoVim, pra quem não sabe, é um fork mais moderno do antigo Vim (por isso o comando é &lt;code&gt;nvim&lt;/code&gt;, reservando &lt;code&gt;vim&lt;/code&gt; pro antigo). Ele troca o antigo VimScripts por scripts em Lua e tem várias otimizações e funcionalidades de qualidade de vida melhores. Hoje em dia, não tem porque usar o Vim antigo mais.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Continuando meus posts de dicas sobre <a href="https://akitaonrails.com/tags/omarchy/"target="_blank" rel="noopener">Omarchy</a> resolvi postar sobre <a href="https://www.lazyvim.org/"target="_blank" rel="noopener">LazyVim</a> que já vem pré-instalado.</p>
<p><img src="https://user-images.githubusercontent.com/292349/213447056-92290767-ea16-430c-8727-ce994c93e9cc.png" alt="LazyVim"  loading="lazy" /></p>
<p>Mesmo se não usa Omarchy, recomendo <a href="https://www.lazyvim.org/installation"target="_blank" rel="noopener">instalar</a> LazyVim, só fazer isso:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># required
mv ~/.config/nvim{,.bak}

# optional but recommended
mv ~/.local/share/nvim{,.bak}
mv ~/.local/state/nvim{,.bak}
mv ~/.cache/nvim{,.bak}

git clone https://github.com/LazyVim/starter ~/.config/nvim

rm -rf ~/.config/nvim/.git

nvim</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pronto, é só isso. NeoVim, pra quem não sabe, é um fork mais moderno do antigo Vim (por isso o comando é <code>nvim</code>, reservando <code>vim</code> pro antigo). Ele troca o antigo VimScripts por scripts em Lua e tem várias otimizações e funcionalidades de qualidade de vida melhores. Hoje em dia, não tem porque usar o Vim antigo mais.</p>
<p>Usando Lua dá pra configurar o Nvim como quiser. Mas dá muito trabalho configurar tudo do zero. É um bom exercício, mas sendo prático, não compensa pro dia a dia. É melhor instalar um setup pré-configurado como os antigos <a href="https://www.lunarvim.org/"target="_blank" rel="noopener">LunarVim</a> que eu usava antes e até recomendei em alguns videos de Linux do meu canal, ou o <a href="https://astronvim.com/"target="_blank" rel="noopener">AstroNvim</a> ou o <strong>LazyVim</strong> que atualmente é meu favorito. Qualquer um deles é um bom começo e vai ser preferência pessoal qual vai escolher.</p>
<p>Cada plugin que eles instalam tem diversas funcionalidades e combinações de teclas (hotkeys) próprias que precisa ler na documentação pra saber o que faz o que. Recomendo ir na seção <a href="https://www.lazyvim.org/plugins"target="_blank" rel="noopener">plugins</a> do site oficial e ler um por um. Tem muita coisa que você nem espera e todos são muito úteis.</p>
<p>LazyVim sozinho já ajuda bastante porque ele abre pop-ups documentando algumas das hotkeys. Por exemplo, o que se chama de comando <strong>Leader</strong> em LazyVim é a &ldquo;barra de espaço&rdquo;. Quando apertar, vai aparecer esse menu de contexto:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/screenshot-2025-09-07_15-44-17.png" alt="Leader context menu"  loading="lazy" /></p>
<p>A partir daqui, se digitar, digamos &ldquo;b&rdquo;, vai aparece outro menu de contexto:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/screenshot-2025-09-07_15-46-48.png" alt="Buffer context menu"  loading="lazy" /></p>
<p>E se digitar &ldquo;b&rdquo; de novo, ele vai trocar pro próximo arquivo já aberto no Buffer. <strong>Buffer</strong> é o nome em Vim onde ficam os arquivos já abertos. Deletar do Buffer é o equivalente a &ldquo;fechar o arquivo&rdquo;.</p>
<p>Da próxima vez, não precisa olhar os menus, só digitar rápido de uma só vez: &ldquo;Espaço+b+b&rdquo;. E é assim que você vai decorando os comandos que mais usa.</p>
<p>Alguns comandos que vale a pena já saber são: <strong>Espaço+Espaço&quot;</strong> (duas vezes), e vai abrir o menu que abre arquivos. digite algumas letras do nome do arquivo e vai filtrando dinamicamente:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/screenshot-2025-09-07_15-49-35.png" alt="find files"  loading="lazy" /></p>
<p>Ou então &ldquo;Espaço+e&rdquo; pra abrir o Explorer NeoTree. Digite &ldquo;?&rdquo; pra abrir um popup com os comandos que pode usar pra criar novos sub-diretórios ou arquivos no subdiretório:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/screenshot-2025-09-07_15-51-08.png" alt="NeoTree"  loading="lazy" /></p>
<p>Quando aprender os demais comandos e plugins, vai notar que nem precisa abrir NeoTree, ele é mais pra quando quiser explorar visualmente mesmo. Mas pro dia a dia comando como &ldquo;:e&rdquo; devem ser suficiente. Aliás, &ldquo;:&rdquo; (dois pontos) é o comando pra executar funções do NeoVim. Por exemplo &ldquo;:q&rdquo; é quit, ou &ldquo;:w&rdquo; é write e &ldquo;:wq&rdquo; é write e quit ao mesmo tempo.</p>
<p>Se quiser aprender mais a fundo sobre NeoVim, recomendo o canal do YouTube do camarada <a href="https://www.youtube.com/@EustaquioRangel"target="_blank" rel="noopener">Eustáquio Rangel</a> que é veterano de Linux, Rails e Vim e ele ensina NeoVim do zero (sem LazyVim).</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/screenshot-2025-09-07_16-09-53.png" alt="TaQ"  loading="lazy" /></p>
<p>Agora, digamos que já aprendeu o básico de NeoVim e LazyVim, e agora quer começar a mexer nos seus projetos de PHP, Java, C#, Ruby, Python ou o que for. Vai notar que o LazyVim não tem nenhuma funcionalidade pra nenhuma delas.</p>
<p>O que fazer agora? Instalar VS Code?? <strong>NÃO!!</strong></p>
<p>Isso porque você não executou <a href="https://www.lazyvim.org/extras"target="_blank" rel="noopener">:LazyExtras</a>.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/screenshot-2025-09-07_15-35-36.png" alt="LazyExtras command"  loading="lazy" /></p>
<p>Digite exatamente assim e veja isto:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/screenshot-2025-09-07_15-36-01.png" alt="Enabled Plugins"  loading="lazy" /></p>
<p>Essa janela parece complicada mas é só uma lista de plugins dividida em seções. No começo aparecem os plugins já instalados e habilitados. Caso não queira algum deles, basta usar as setas pra descer o cursor até uma delas e apertar &ldquo;x&rdquo; pra desmarcar. Mas deixe essa seção como está. Vamos descer até a seção de &ldquo;Languages&rdquo;:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/screenshot-2025-09-07_15-36-43.png" alt="Languages section"  loading="lazy" /></p>
<p>É nesta seção que estão todas as linguagens que você pode habilitar o suporte. Basta descer com as setas e ir marcando com &ldquo;x&rdquo; quais quer instalar. E tem de tudo, desde Angular, Vue, Erlang, Elixir, Rust, Zig, Haskell e muito mais.</p>
<p>Ao lado de cada linguagem tem o grupo de plugins individuais que ele vai instalar. Pra saber o que cada uma faz, digite o nome delas no Google e ache a página com a documentação, onde vai ter hotkeys e configurações extras que pode fazer.</p>
<p>Com isso vai ganhar coisas como Linters, syntax highlighting, LSP, snippets e muito mais.</p>
<p>E se quiser instalar plugins a mais que não tem listado, dá pra adicionar manualmente. Leia a seção de <a href="https://www.lazyvim.org/configuration/plugins#-adding-plugins"target="_blank" rel="noopener">Adding Plugins</a> na documentação.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/screenshot-2025-09-07_16-05-15.png" alt="Linter"  loading="lazy" /></p>
<p>LazyVim + LazyExtras é 90% de tudo que precisa pro seu dia a dia de desenvolvimento de software. É mais leve e mais rápido que um VS Code, usando muito menos memória e vai ser muito melhor especialmente em máquinas mais antigas. Não tenha preguiça de aprender Vim, um programador de verdade não deveria ficar num VS Code só porque tem preguiça de aprender hotkeys extras.</p>
]]></content:encoded><category>omarchy</category><category>ferramentas-de-desenvolvimento</category><category>tutoriais</category></item><item><title>Como Contribuir no Blog do AkitaOnRails usando Docker</title><link>https://akitaonrails.github.io/2025/08/29/como-contribuir-no-blog-do-akitaonrails-usando-docker/</link><guid isPermaLink="true">https://akitaonrails.github.io/2025/08/29/como-contribuir-no-blog-do-akitaonrails-usando-docker/</guid><pubDate>Fri, 29 Aug 2025 22:37:16 -0300</pubDate><description>&lt;p&gt;Contribuir em projetos open source pode ser um saco quando você precisa configurar um ambiente de desenvolvimento complexo. No blog do AkitaOnRails, isso significava instalar Hugo Extended, Go, Ruby e suas dependências. Mas agora temos uma solução muito mais simples: &lt;strong&gt;Docker&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;O Problema&lt;span class="hx:absolute hx:-mt-20" id="o-problema"&gt;&lt;/span&gt;
&lt;a href="#o-problema" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Antes, pra contribuir no blog você precisava:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Instalar Hugo Extended&lt;/li&gt;
&lt;li&gt;Instalar Go&lt;/li&gt;
&lt;li&gt;Instalar Ruby&lt;/li&gt;
&lt;li&gt;Configurar todas as dependências&lt;/li&gt;
&lt;li&gt;Gerar o índice de posts manualmente&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Isso criava barreiras pra novos contribuidores e podia causar conflitos com outras instalações no sistema. Um inferno.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Contribuir em projetos open source pode ser um saco quando você precisa configurar um ambiente de desenvolvimento complexo. No blog do AkitaOnRails, isso significava instalar Hugo Extended, Go, Ruby e suas dependências. Mas agora temos uma solução muito mais simples: <strong>Docker</strong>.</p>
<h2>O Problema<span class="hx:absolute hx:-mt-20" id="o-problema"></span>
    <a href="#o-problema" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Antes, pra contribuir no blog você precisava:</p>
<ol>
<li>Instalar Hugo Extended</li>
<li>Instalar Go</li>
<li>Instalar Ruby</li>
<li>Configurar todas as dependências</li>
<li>Gerar o índice de posts manualmente</li>
</ol>
<p>Isso criava barreiras pra novos contribuidores e podia causar conflitos com outras instalações no sistema. Um inferno.</p>
<h2>A Solução: Docker<span class="hx:absolute hx:-mt-20" id="a-solução-docker"></span>
    <a href="#a-solu%c3%a7%c3%a3o-docker" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Agora você só precisa do Docker e Docker Compose. Tudo o resto roda em containers isolados.</p>
<h3>Setup Rápido<span class="hx:absolute hx:-mt-20" id="setup-rápido"></span>
    <a href="#setup-r%c3%a1pido" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Clone o repositório</span>
</span></span><span class="line"><span class="cl">git clone https://github.com/akitaonrails/akitaonrails.github.io.git
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> akitaonrails.github.io
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Inicie o ambiente</span>
</span></span><span class="line"><span class="cl">./scripts/dev.sh start
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Acesse o blog</span>
</span></span><span class="line"><span class="cl">open http://localhost:1313</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pronto! O blog está rodando e você pode começar a contribuir.</p>
<h2>Comandos Úteis<span class="hx:absolute hx:-mt-20" id="comandos-úteis"></span>
    <a href="#comandos-%c3%bateis" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O projeto inclui scripts que facilitam o desenvolvimento:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Ver todos os comandos disponíveis</span>
</span></span><span class="line"><span class="cl">./scripts/dev.sh <span class="nb">help</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Criar um novo post</span>
</span></span><span class="line"><span class="cl">./scripts/dev.sh new-post <span class="s2">&#34;Título do Meu Post&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Ver logs do servidor</span>
</span></span><span class="line"><span class="cl">./scripts/dev.sh logs
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Gerar índice de posts</span>
</span></span><span class="line"><span class="cl">./scripts/dev.sh generate-index
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Parar o ambiente</span>
</span></span><span class="line"><span class="cl">./scripts/dev.sh stop</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h2>Criando um Post<span class="hx:absolute hx:-mt-20" id="criando-um-post"></span>
    <a href="#criando-um-post" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Criar um novo post é super simples:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">./scripts/dev.sh new-post <span class="s2">&#34;Como Contribuir no Blog do AkitaOnRails&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O script automaticamente:</p>
<ul>
<li>Cria a estrutura de diretórios com a data atual</li>
<li>Gera um arquivo <code>index.md</code> com front matter básico</li>
<li>O servidor Hugo recarrega automaticamente</li>
</ul>
<h2>Estrutura de um Post<span class="hx:absolute hx:-mt-20" id="estrutura-de-um-post"></span>
    <a href="#estrutura-de-um-post" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O arquivo criado terá esta estrutura:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-markdown" data-lang="markdown"><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">title: &#34;Título do Post&#34;
</span></span><span class="line"><span class="cl">date: &#34;2025-08-29&#34;
</span></span><span class="line"><span class="cl">draft: false
</span></span><span class="line"><span class="cl">description: &#34;Descrição do post&#34;
</span></span><span class="line"><span class="cl">tags: [tag1, tag2]
</span></span><span class="line"><span class="cl">categories: [categoria]
</span></span><span class="line"><span class="cl">---
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Conteúdo do post aqui...</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h2>Vantagens do Docker<span class="hx:absolute hx:-mt-20" id="vantagens-do-docker"></span>
    <a href="#vantagens-do-docker" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><h3>Para Contribuidores<span class="hx:absolute hx:-mt-20" id="para-contribuidores"></span>
    <a href="#para-contribuidores" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><strong>Setup rápido</strong>: Apenas Docker necessário</li>
<li><strong>Ambiente isolado</strong>: Não interfere com sistema local</li>
<li><strong>Hot reload</strong>: Mudanças aparecem automaticamente</li>
<li><strong>Reproduzível</strong>: Funciona igual para todos</li>
</ul>
<h3>Para o Projeto<span class="hx:absolute hx:-mt-20" id="para-o-projeto"></span>
    <a href="#para-o-projeto" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li><strong>Menos issues</strong>: Ambiente consistente</li>
<li><strong>Mais contribuidores</strong>: Barreira de entrada menor</li>
<li><strong>Manutenção</strong>: Fácil de atualizar dependências</li>
</ul>
<h2>Como Funciona<span class="hx:absolute hx:-mt-20" id="como-funciona"></span>
    <a href="#como-funciona" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>O ambiente Docker inclui:</p>
<ul>
<li><strong>Hugo Extended</strong>: Gerador de sites estáticos</li>
<li><strong>Go</strong>: Para módulos Hugo</li>
<li><strong>Ruby</strong>: Para script de geração de índice</li>
<li><strong>Volumes</strong>: Para hot reload dos arquivos</li>
</ul>
<p>O <code>docker-compose.yml</code> mapeia os diretórios locais para o container, então quando você edita um arquivo, o Hugo detecta a mudança e recarrega automaticamente.</p>
<h2>Desenvolvimento Local (Alternativa)<span class="hx:absolute hx:-mt-20" id="desenvolvimento-local-alternativa"></span>
    <a href="#desenvolvimento-local-alternativa" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Se preferir não usar Docker, ainda é possível instalar tudo localmente:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># Gerar índice</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> content <span class="o">&amp;&amp;</span> ruby generate_index.rb
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Rodar servidor</span>
</span></span><span class="line"><span class="cl">hugo server --logLevel debug --disableFastRender -p <span class="m">1313</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Mas o Docker é muito mais prático!</p>
<h2>Próximos Passos<span class="hx:absolute hx:-mt-20" id="próximos-passos"></span>
    <a href="#pr%c3%b3ximos-passos" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ol>
<li><strong>Teste o ambiente</strong>: Execute <code>./scripts/dev.sh start</code></li>
<li><strong>Crie um post</strong>: Use <code>./scripts/dev.sh new-post</code></li>
<li><strong>Explore o código</strong>: Veja como personalizar layouts</li>
<li><strong>Contribua</strong>: Faça um Pull Request</li>
</ol>
<hr>
<p><strong>Dica</strong>: O ambiente Docker salva automaticamente suas mudanças e recarrega o servidor. Basta editar os arquivos e ver as mudanças em tempo real! 🚀</p>
]]></content:encoded><category>open-source</category><category>containers</category><category>tutoriais</category></item><item><title>Dissecando um Modelfile de Ollama - Ajustando Qwen3 pra código</title><link>https://akitaonrails.github.io/2025/04/29/dissecando-um-modelfile-de-ollama-ajustando-qwen3-pra-codigo/</link><guid isPermaLink="true">https://akitaonrails.github.io/2025/04/29/dissecando-um-modelfile-de-ollama-ajustando-qwen3-pra-codigo/</guid><pubDate>Tue, 29 Apr 2025 02:30:00 -0300</pubDate><description>&lt;p&gt;A notícia quente do dia é o lançamento do novo modelo Qwen3. Eu mesmo &lt;a href="https://www.akitaonrails.com/2025/04/28/testando-o-recem-lancado-llm-open-source-qwen3-com-aider-e-ollama"target="_blank" rel="noopener"&gt;postei a respeito hoje&lt;/a&gt; E já fiquei bem impressionado. Achei que ia deixar por isso mesmo, mas aí vi este tweet:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/1jgq3ar5c5l7wfjt9wjfui8l598z" alt="ivan qwen tweaks" loading="lazy" /&gt;&lt;/p&gt;
&lt;p&gt;Veja o &lt;a href="https://x.com/ivanfioravanti/status/1916934241281061156"target="_blank" rel="noopener"&gt;texto completo neste link&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mas ele dá dicas de como &amp;ldquo;tunar&amp;rdquo; esse novo modelo, mais pra questões matemáticas e fiquei pensando se tem como tunar pra desenvolvimento de software. Antes que pesquisadores mais avançados me xinguem: essa é minha primeira vez mexendo nos parâmetros de uma LLM, então se quiserem adicionar contexto nos comentários, sejam bem vindos.&lt;/p&gt;</description><content:encoded><![CDATA[<p>A notícia quente do dia é o lançamento do novo modelo Qwen3. Eu mesmo <a href="https://www.akitaonrails.com/2025/04/28/testando-o-recem-lancado-llm-open-source-qwen3-com-aider-e-ollama"target="_blank" rel="noopener">postei a respeito hoje</a> E já fiquei bem impressionado. Achei que ia deixar por isso mesmo, mas aí vi este tweet:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/1jgq3ar5c5l7wfjt9wjfui8l598z" alt="ivan qwen tweaks"  loading="lazy" /></p>
<p>Veja o <a href="https://x.com/ivanfioravanti/status/1916934241281061156"target="_blank" rel="noopener">texto completo neste link</a></p>
<p>Mas ele dá dicas de como &ldquo;tunar&rdquo; esse novo modelo, mais pra questões matemáticas e fiquei pensando se tem como tunar pra desenvolvimento de software. Antes que pesquisadores mais avançados me xinguem: essa é minha primeira vez mexendo nos parâmetros de uma LLM, então se quiserem adicionar contexto nos comentários, sejam bem vindos.</p>
<p>Vamos lá. Primeiro, o que são esses TopP, TopK e tudo mais que o Ivan fala no tweet dele? São parâmetros de <strong>KEY SAMPLING</strong> (sorteio de chaves). É um grupo de parâmetros que controlam <em>COMO</em> o modelo seleciona o <em>PRÓXIMO TOKEN</em> e vai completando o texto (dando a &ldquo;resposta&rdquo;), a partir do seu vocabulário baseado em distribuição de probabilidade que ele calcula. Os mais comuns são justamente <code>temperature</code>, <code>top_p</code>, <code>top_k</code> e algumas vezes <code>min_p</code> ou <code>repeat_penalty</code>.</p>
<p>Eu sempre falo: LLM é um gerador de próxima palavra, um completador de texto baseado em probabilidades. Quando a LLM processa seu input (seu contexto, histórico do chat e a próxima pergunta), ele precisa calcular o &ldquo;score&rdquo; de probabilidade pra cada palavra ou sub-palavra (token) no seu vocabulário inteiro. Imagina um modelo gigante, ele precisa recalcular pra tudo o tempo todo, pra cada próxima palavra.</p>
<p>Parâmetros de sampling dizem pro modelo &ldquo;como escolher o próximo token&rdquo; da lista de probabilidades. Sem esses parâmetros o modelo escolheria um decodificador &ldquo;greedy&rdquo; (ambicioso demais). Significa que ele ia só pegar o token com a maior probabilidade toda vez. E isso leva a um resultado repetitivo e que soa pouco natural. Sampling adiciona um tipo de &ldquo;aleatoriedade controlada&rdquo; (lembram? eu sempre falo que tem aleatoriedade misturado, nunca a resposta é &ldquo;certa&rdquo;), e isso torna a resposta mais &ldquo;interessante&rdquo; pra um ser humano (facilmente enganável). Eis alguns dos parâmetros:</p>
<h3>Temperatura<span class="hx:absolute hx:-mt-20" id="temperatura"></span>
    <a href="#temperatura" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Controla a aleatoriedade, que muitos chamam de &ldquo;criatividade&rdquo;. Essencialmente torna a distribuição mais quente (mais espalhada e mais plana) ou mais fria (com mais picos ao redor dos mesmos tokens). Quanto maior a temperatura (até 1.0) isso aumenta a probabilidade de escoher tokens que tinham probabilidades calculadas menores. Quanto menor a temperatura (até 0.0) menores as chances dos tokens calculados com menor probabilidade, mais &ldquo;greedy&rdquo; vai ser o resultado (menos &ldquo;humano&rdquo;).</p>
<p>Pra código, deixe muito alto e aí que surgem &ldquo;alucinações&rdquo; (que é basicamente aleatoriedade aumentada) e código que não faz sentido. Se for zero, vai ser muito &ldquo;boilerplate&rdquo;, repetitivo, usando padrões muito comuns e pode potencialmente acabar num loop infinito sem resposta (porque ele não consegue puxar nada diferente).</p>
<p>O tweet sugere 0.6 que é moderado. Mas nos meus testes, temperatura tão alta começou já a me dar uns códigos esquisitos. Do nada ele mudava nome de variável. Do nada aparecia um caracter chinês no meio do código (literalmente). Eu acho que 0 é muito baixo, mas 0.1 é suficiente, talvez 0.2.</p>
<p>Isso você pode mudar no arquivo <code>~/.aider.model.settings.yaml</code> antes de carregar o modelo (esses parâmetros podem ser reajustados na inicialização):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">ollama_chat/qwen3-dev:latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">extra_params</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">temperature</span><span class="p">:</span><span class="w"> </span><span class="m">0.1</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">num_ctx</span><span class="p">:</span><span class="w"> </span><span class="m">40960</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h3>top_p (Nucleus Sampling)<span class="hx:absolute hx:-mt-20" id="top_p-nucleus-sampling"></span>
    <a href="#top_p-nucleus-sampling" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Aqui vai complicando. Esse <code>top_p</code> serve pra selecionar tokens do menor conjunto de tokens cuja probabilidade cumulativa exceda esse limite &ldquo;p&rdquo;. O modelo então puxa samples somente desse conjunto reduzido.</p>
<p>Esse modelo ordena todos os tokens por suas probabilidades em ordem descendente (do maior pro menor). Então começa a adicionar suas probabilidades até a soma ser maior ou igual a <code>top_p</code>. Todos os tokens incluídos na soma, formam o tal &ldquo;nucleus&rdquo; e desse conjunto ele sorteia um próximo token. Então é um sorteio dos tokens com sum(x) maiores probabilidades, mais ou menos. &ldquo;Escolha um dos vencedores, mas não necessariamente O vencedor (que seria greedy)&rdquo;.</p>
<h3>top_k<span class="hx:absolute hx:-mt-20" id="top_k"></span>
    <a href="#top_k" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Considere somente os &ldquo;k&rdquo; tokens mais prováveis. O modelo só sorteia (sample) desse conjunto reduzido dos top K maiores probabilidades de tokens. Isso é realmente dizer &ldquo;escolha um dentre os Top 10 melhores&rdquo;. Quanto maior o K maior a diversidade, quanto menor mais greedy.</p>
<p>E isso é relativo, top_k e top_p agem em conjunto. Essa configuração ajuda a ancorar a seleção aos tokens &ldquo;mais prováveis&rdquo; enquanto ainda permite ao top_p filtrar e tirar os de menor probabilidade. Isso prioriza plausibilidade (top_p) enquanto restringe o número total de tokens sorteados (top_k). Ajuda a manter o resultado focado nas respostas mais possivelmente &ldquo;certas&rdquo; (mas de novo, é um sorteio/sampling).</p>
<h3>min_p<span class="hx:absolute hx:-mt-20" id="min_p"></span>
    <a href="#min_p" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Filtra e tira tokens cuja probabilidade são menores que &ldquo;min_p&rdquo; vezes a probabilidade do token mais provável. Antes do sorteio, o modelo identifica o token com maior probabilidade (P_max), então remove qualquer token com probabilidade (P_token) que é menor que <code>P_max * min_p</code>. O sorteio então acontece a partir da lista que sobra (que vai ser mais filtrada por top_p e top_k).</p>
<p>Isso previne que o modelo escolha tokens que são &ldquo;extremamente improváveis&rdquo; relativo à escolha mais óbvia.</p>
<h3>repeat_penalty<span class="hx:absolute hx:-mt-20" id="repeat_penalty"></span>
    <a href="#repeat_penalty" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Isso desencoraja o modelo de repetir os mesmos tokens que já apareceram recentemente no histórico da conversa ou texto gerado.</p>
<p>Antes do sorteio, a pontuação de probabilidade dos tokens que já foram vistos recentemente são reduzidas, dividindo por essa <code>repeat_penalty</code> (com valores maiores que 1, já que dividir por 1 é ele mesmo, então mantém a mesma probabilidade).</p>
<p>Se deixa 1.0, pode começar a repetir as mesmas respostas ou os mesmos códigos, até os errados. Se for alto demais, muito acima de 1.0, a resposta pode ser pouco natural, já que às vezes precisa mesmo repetir alguma coisa pra re-explicar. Um valor moderado lá pelo 1.1 a menos de 1.2 pode ser mais adequado pra não punir demais o que já está no histórico.</p>
<h3>num_predict (max tokens pra gerar)<span class="hx:absolute hx:-mt-20" id="num_predict-max-tokens-pra-gerar"></span>
    <a href="#num_predict-max-tokens-pra-gerar" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O absoluto máximo de tokens que o modelo vai gerar de resposta. Se passar, vai truncar. Se for um número pequeno demais, especialmente em resposta de código - que pode ser longo - pode truncar demais e deixar resposta incompleta. Se for grande demais vai aumentar muito o processamento e vai exigir muito mais recursos da máquina e mais tempo e pode ser um desperdício.</p>
<p>Um valor de uns 32 mil tokens tem bastante espaço pra tokens. Tem que testar, e varia caso a caso pro tipo de resposta que você espera e o que cada modelo suporta.</p>
<h3>Recomendado<span class="hx:absolute hx:-mt-20" id="recomendado"></span>
    <a href="#recomendado" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Sabendo tudo isso agora você também consegue ler o tweet do Ivan e entender. Ele  recomenda Temp 0.6, TopP 0.95, TopK 20, MinP 0.</p>
<p>Eu mexi mais um pouco. E eu acho que pra código, Temp tem que ser na faixa de 0.1 ou 0.2. Também acho que vale um &ldquo;repeat_penalty&rdquo; de 1.1 pra evitar repetições desnecessárias, especialmente de código. De novo, tem que testar. São números empíricos e não exatos.</p>
<p>Eu criei um <a href="https://github.com/akitaonrails/qwen3-dev"target="_blank" rel="noopener">projeto no meu GitHub</a> com um arquivo completo de modelo que implementa não só esses parâmetros mas já pré-configura um prompt de sistema pra enviesar o modelo a ser um assistente de código primeiro. Eis meu prompt:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>SYSTEM &#34;&#34;&#34;
You are a highly functional AI assistant specializing in software development tasks.
Respond to queries about coding, algorithms, debugging, system design,
and programming languages/frameworks.
Provide clear, accurate, step-by-step reasoning and functional code examples.
Always format code snippets using markdown triple backticks (```language ... ```).
Aim for comprehensive and correct technical assistance.
Never suggest changing code unrelated to the question topic at hand.
Restrict yourself to the minimum amount of changes that do resolve the problem at hand.
Avoid renaming functions or variables unless absolutely necessary, 
and in doing so make sure if there&#39;s anyone calling the old names, 
for them to be renamed to the new names. Never leave the code in a broken state. 
Pay close attention to correctness, not just answering the quickiest and dirtiest code to solve the problem.
&#34;&#34;&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pra fazer esse arquivo, primeiro eu puxei o Modelfile original do Qwen3 com este comando:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ollama show qwen3 --modelfile &gt; Modelfile</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>É importante fazer isso porque, pelo que entendi, modelfiles não herdam do anterior. Não é que nem um &ldquo;FROM&rdquo; de um Dockerfile, onde você continua uma nova imagem a partir da base anterior.</p>
<p>Depois de editado com meus ajustes - que já está no meu GitHub, é só criar um novo modelo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>ollama create qwen3-dev -f qwen3-dev.modelfile</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E ele vai aparecer no seu ollama local. Veja com <code>ollama list</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>❯ ollama list
NAME                                            ID              SIZE      MODIFIED
qwen3-dev:latest                                fe03dfae5484    9.3 GB    About an hour ago
qwen3:32b                                       e1c9f234c6eb    20 GB     2 hours ago
qwen3:14b                                       7d7da67570e2    9.3 GB    8 hours ago
qwen3:latest                                    e4b5fd7f8af0    5.2 GB    8 hours ago
qwen2.5-coder:7b-instruct                       2b0496514337    4.7 GB    25 hours ago
....</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Viu o <code>qwen3-dev:latest</code>? É nosso novo modelo com ajustes de parâmetros. No aider é só usar ele direto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>aider --watch-files --model ollama_chat/qwen3-dev:latest</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Lembrando de ajustar o <code>.aider.model.settings.yml</code> como já mostrei acima.</p>
<p>Eu não fiz testes longos ainda, mas resolvi brincar com o modelo 14b como base e esses ajustes. O modelo base já me impressionou e pelo menos meus ajustes não quebraram nada. Fiquei fazendo os mesmos refatoramentos e testes unitários no meu projetinho educativo e subi mais um <a href="https://github.com/akitaonrails/Tiny-Qwen-CLI/pull/6"target="_blank" rel="noopener">pull request</a></p>
<p>Eu acho que o Gemini 2.5 Pro Exp é um pouco melhor, mas de novo, não por uma margem muito grande. E em alguns casos (como na refatoração), eu acho que prefiro o código gerado pelo Qwen3. Compare com o código que consegui com o Gemini, neste <a href="https://github.com/akitaonrails/Tiny-Qwen-CLI/pull/3"target="_blank" rel="noopener">outro pull request</a></p>
<p>Com o Gemini eu precisei explicar menos e ele deu menos erros, mas ambos conseguiram fazer o que eu pedi até o fim e sem quebrar o programa principal. Mas o Qwen3 me parece bom o suficiente pra eu querer usar ele. Com a vantagem que mesmo sendo o modelo médio, de 14 bilhões de parâmetros (que roda na minha VRAM de 24GB com sobra), ele ainda dá resultados muito bons e limpos de código. E melhor: roda MUITO, mas MUITO mais rápido que o Qwen2.5 32b.</p>
<p>Pra quem tem GPUs com menos VRAM, digamos, uma 3070 ou 4070 de 16GB, dá pra usar o modelo menor ainda, de 7b. Eu testei o qwen2.5-coder:7b e tive resultados excelentes, então imagino que o Qwen3 de 7b deve ser ainda melhor e muito mais rápido. Velocidade que é comparável ao Claude ou Gemini, que são muito maiores e rodam em máquina muito mais parruda que uma RTX 4070.</p>
<p>Rodando meu qwen3-dev, no meu monitoramento minha CPU ficava maior parte do tempo em idle e a GPU o tempo todo acima de 90%, consumindo mais quase 400W constantemente, mantendo temperatura de 73 graus (o que é bom).</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/jv8ub1bc6m70u1agm1nyiooav2mk" alt="btop"  loading="lazy" /></p>
<p>Tive até um pequeno incidente onde meu PC desligou do nada, porque a extensão temporária que eu estava usando não aguentou e morreu. Somado com a CPU e meus outros dispositivos ligados na mesma tomada, estava puxando consistentemente 1000W da parede por horas. Brincar de I.A. usa <strong>MUITA</strong> energia, com ar-condicionado ligado porque só pra dissipar tudo isso, meu apartamento fica 1 a 2 graus (que eu chuto) acima do normal. Dá pra sentir mais quentinho mesmo kkkk</p>
<p>Mas é isso, espero que tenham entendido como que as coisas funcionam de verdade por baixo dos panos. Não tem mágica: é geração de próxima palavra, baseada em sorteio semi-aleatório de probabilidades.</p>
<p>Now you know.</p>
]]></content:encoded><category>llms</category><category>modelos-locais</category><category>tutoriais</category></item><item><title>Entendendo o Básico de ComfyUI pra gerar imagens com I.A.</title><link>https://akitaonrails.github.io/2025/04/20/entendendo-o-basico-de-comfyui-pra-gerar-imagens-com-i-a/</link><guid isPermaLink="true">https://akitaonrails.github.io/2025/04/20/entendendo-o-basico-de-comfyui-pra-gerar-imagens-com-i-a/</guid><pubDate>Sun, 20 Apr 2025 19:30:00 -0300</pubDate><description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Atualização:&lt;/strong&gt; este post está obsoleto. Substitui a configuração manual por um projeto containerizado mais limpo: &lt;a href="https://github.com/akitaonrails/aitrepreneur-docker"target="_blank" rel="noopener"&gt;&lt;strong&gt;akitaonrails/aitrepreneur-docker&lt;/strong&gt;&lt;/a&gt;. Ele roda as stacks do Aitrepreneur localmente em Docker, com versões fixadas, modelos persistentes e workflows pré-carregados. Veja o README para detalhes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;No &lt;a href="https://www.akitaonrails.com/2025/04/20/gerando-imagens-com-i-a-ate-estilo-ghibli-com-docker-e-cuda"target="_blank" rel="noopener"&gt;post anterior&lt;/a&gt; expliquei meu projetinho de como subir ComfyUI pré-configurado e pré-carregado automaticamente usando Docker Compose. Assumindo que você já tem tudo de pé, agora é entender essa desgraça do ComfyUI um pouco.&lt;/p&gt;</description><content:encoded><![CDATA[<blockquote>
  <p><strong>Atualização:</strong> este post está obsoleto. Substitui a configuração manual por um projeto containerizado mais limpo: <a href="https://github.com/akitaonrails/aitrepreneur-docker"target="_blank" rel="noopener"><strong>akitaonrails/aitrepreneur-docker</strong></a>. Ele roda as stacks do Aitrepreneur localmente em Docker, com versões fixadas, modelos persistentes e workflows pré-carregados. Veja o README para detalhes.</p>

</blockquote>
<p>No <a href="https://www.akitaonrails.com/2025/04/20/gerando-imagens-com-i-a-ate-estilo-ghibli-com-docker-e-cuda"target="_blank" rel="noopener">post anterior</a> expliquei meu projetinho de como subir ComfyUI pré-configurado e pré-carregado automaticamente usando Docker Compose. Assumindo que você já tem tudo de pé, agora é entender essa desgraça do ComfyUI um pouco.</p>
<p>Eu também não sou nenhum especialista, mas achei legal explicar alguns conceitos que muitos ignoram.</p>
<h3>Modelos Pré-Treinados<span class="hx:absolute hx:-mt-20" id="modelos-pré-treinados"></span>
    <a href="#modelos-pr%c3%a9-treinados" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Todo GPT da vida, ou Stable Diffusion ou outros carregam um banco de dados vetorial na memória da GPU, na VRAM, pra processar. São modelos GRANDES, chamados de 7B, 70B, 100B, etc. &ldquo;B&rdquo; de &ldquo;Bilhões de Parâmetros&rdquo;. Parâmetros não tem definição usável fora da matemática, são simples números. Esses números representam &ldquo;alguma coisa&rdquo; dentro milhões de dimensões dentro desse arquivão.</p>
<p>Em um jeito BEM GROSSEIRO, eu penso como sendo artefatos de compressão. Você pega PETABYTES de livros, imagens, textos, posts, etc, quebra tudo em tokens e encontra &ldquo;relacionamentos&rdquo; de um token com vários tokens e vai &ldquo;posicionando&rdquo; no espaço, mas não em 3 dimensões, mas milhões de dimensões. Modelos de I.A. são &ldquo;Vector Space Models&rdquo; ou VSMs, mais ou menos como é o índice de um Elasticsearch se você já brincou com procura de texto com relevância, ou qualquer plugin de &ldquo;Full Text Search&rdquo; de um Postgres ou MS-SQL. São todos VSMs.</p>
<p>Eu penso assim: é como se o processo de treinamento - a parte SUPER CARA, que leva MESES - pegasse toda informação que é passada e compilasse um novo &ldquo;dicionário&rdquo; pra uma nova &ldquo;língua&rdquo;. Agora, quando você &ldquo;fala&rdquo; com um GPT, primeiro ele precisa transformar seu texto da sua língua pra essa &ldquo;língua interna&rdquo; dele, isso é o que chamamos de um <strong>&ldquo;Embedding&rdquo;</strong>.</p>
<p>Qualquer dado pode ser transformado. Áudio pode ser convertido em texto e de texto em embeddings. Videos podem ser desmonstados em séries de imagens com informação temporal. Imagens podem ser desconstruídas em elementos como luminância, crominância, etc. No fim, tudo pode ser desconstruído em estruturas de dados, não tem mágica.</p>
<p>Modelos de Geração de Imagem são treinados diferente. Tem diversos focos, foco em nitidez de rostos, foco em exatidão de poses, foco em estilos diferentes como cartoon ou pintura, foco em categorias como animais ou objetos. Por isso tem dezenas de modelos por aí e diversos &ldquo;loras&rdquo;.</p>
<p>Pra gerar uma imagem a partir de um prompt, primeiro precisa decodificar seu prompt num embedding, pra isso servem coisas como um <strong>TEXT ENCODER</strong> no ComfyUI.</p>
<p>No caso específico de I.A. pra imagens, eu entendo que se usa um tal de modelo de &ldquo;DIFUSÃO&rdquo; tanto pra desestruturar a imagem original, quanto pra reconstruir uma nova imagem. Não é intuitivo, mas a reconstrução não começa numa tela branca, como seria pra um desenhista humano. Ele começa com uma imagem com barulho aleatório &ldquo;noise&rdquo; tipo isso aqui:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/rcvhb8m0boe3jetpy72kww2wmucw" alt="noise"  loading="lazy" /></p>
<p>O modelo começa desse barulho e via um tal processo de DIFUSÃO começa a redesenhar a imagem de trás pra frente, até sair do caos e chegar numa imagem.</p>
<p>O que eu &ldquo;ACHO&rdquo;, &ldquo;CHUTO&rdquo; que aconteça - sem ter lido paper nenhum, preguiça mesmo - é que tentamos influenciar esse barulho inicial. Por exemplo, extraindo funcionalidades da imagem como um mapa de profundidade, que se parece com isso:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/qj0ba39q5xbs7xyei52h4qmmpr2d" alt="Mapa de Profundidade"  loading="lazy" /></p>
<p>A foto original era assim:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/6zionu0khn01gpudd9z0ws2mm8zx" alt="Original"  loading="lazy" />.jpg)</p>
<p>Extrair um Mapa de Profundidade é um <strong>ALGORITMO</strong> e não &ldquo;I.A.&rdquo;. Qualquer Photoshop, Premiere da vida conseguem fazer isso com uma mão nas costas. Mas é pra explicar que tem muito mais informação numa imagem que só olhando você - que não é treinado - não sabe que existe.</p>
<p>Outro tipo de ALGORITMO bem conhecido é o <strong>Canny Edge Detection</strong> que faz um mapa de bordas, pra ficar mais fácil de saber onde alguma coisa começa e onde termina na imagem:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/std6jaroftohqjlc22mii9m7oso6" alt="Canny"  loading="lazy" /></p>
<p>Então é mais ou menos assim:</p>
<ul>
<li>assim como um texto é &ldquo;convertido&rdquo; numa <strong>Embedding</strong>, vetores que capturam a &ldquo;essência&rdquo; do significado do texto, dependente da VSM do modelo pré-treinado, uma imagem é &ldquo;convertida&rdquo; internamente num <strong>LATENT</strong>, que por ser imagem é um &ldquo;espaço&rdquo; (por isso se fala em Latent Space). Latents não temos como &ldquo;visualizar&rdquo;, porque é uma representação interna que depende da U-NET, o modelo pré-treinado, a rede neural que aprende a &ldquo;tirar barulho&rdquo; (denoise) o Latent.</li>
<li>Uma U-NET de imagens é grosseiramente similar a um modelo de texto como GPT ou LLaMA, o modelo pré-treinado, read-only, que vai ser usado pra produzir o resultado final.</li>
<li>Uma resposta de GPT é uma continuação do contexto da conversa anterior. Ele não está &ldquo;respondendo&rdquo; você diretamente, está só &ldquo;continuando&rdquo; o texto da conversa e por acaso, pelo treinamento, o resultado &ldquo;se parece&rdquo; com uma resposta. No caso de imagens, começamos com uma imagem aleatoriamente barulhenta (noise) e num processo de tirar e adicionar barulho, ele vai &ldquo;reorganizando&rdquo; o Latent numa imagem que conseguimos reconhecer.</li>
<li>Existe um &ldquo;Scheduler&rdquo; que decide quanto barulho é adicionado ou removido a cada passo no tempo. E existe o conceito de <strong>DIFFUSION MODEL</strong> que é mais ou menos o &ldquo;pipeline&rdquo; de processamento.</li>
<li>Podemos controlar o processo adicionando um prompt de texto, que é decodificado numa &ldquo;embedding&rdquo; usando um <strong>Text Decoder</strong> normalmente tem nomes como &ldquo;CLIP&rdquo; ou propriamente um Transformer (como LLava). Esses embeddings vão pra U-NET usando camadas de atenção cruzada, direcionando o &ldquo;denoising&rdquo; em direção ao significado do prompt.</li>
<li>Além disso temos <strong>CONTROLNET</strong> que é uma segunda rede paralela, que adapta o processo pra condições extras. É uma cópia da arquitetura de U-NET (com pesos congelados) que recebem inputs extras, como o Canny Edges, ou Depth Maps, esqueletos de pose (via OpenPose), etc e aprende pequenos ajustes pra <strong>INJETAR</strong> nos mapas principais da U-NET.</li>
<li>Durante o processo de denoising, ativações da ControlNet são mescladas às da U-NET, forçando o processo a respeitar as condições que passamos.</li>
</ul>
<p>Então a U-NET é o &ldquo;gerador&rdquo;, o Diffusion Scheduler orquestra o processo, text encoder e contronet, ambos influenciam a U-NET, e um VAE é usado no final pra converter um espaço latente em uma imagem propriamente dita que nós, humanos, conseguimos enxergar.</p>
<p>BEM, BEM A GROSSO MODO. É mais ou menos como eu entendo o processo. E eu entendo que o ComfyUI expõe vários desses passos na forma de &ldquo;NODES PROGRAMÁVEIS&rdquo;, que podemos parametrizar e ajustar pra influenciar como queremos que a imagem final saia.</p>
<p>Além de parâmetros numéricos, esses Nodes também nos deixam escolher quais arquivos de modelos pré-treinados pra cada etapa queremos usar. É tudo um VSM binário, mas pra utilidades diferentes, por isso muitos tem a mesma extensão como eu expliquei, mas ficam em sub-diretórios diferentes. Pra cada Node conseguir achar só o que lhe interessa.</p>
<p>Pegando o exemplo da minha foto, não é isso mas só pra ilustrar, me ajuda a pensar que aqueles mapas que eu mostrei influenciam a imagem &ldquo;noisy&rdquo; numa direção não-aleatória, como este que eu peguei numa etapa intermediária do workflow que estou rodando:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/yy0yrlrcel015bucfr3z60izt42p" alt="noise"  loading="lazy" /></p>
<p>Se forçar seus olhos, consegue ver influência das imagens anteriores, e o modelo de Difusão vai fazendo o &ldquo;denoising&rdquo;, sendo &ldquo;atrapalhado&rdquo; pela ControlNet. Como se você estivesse desenhando e tivesse uma pessoa do lado que de vez em quando apaga o que você fez, ou desenha por cima, e você vai se adaptando. Então no final vira um trabalho &ldquo;colaborativo&rdquo; entre os dois. Se fosse só a U-NET, ele ia gerar uma nova imagem bem diferente da original, mas a ControlNet é o &ldquo;cliente&rdquo; que fica toda hora de buzinando na orelha &ldquo;não é assim que eu quero, muda&rdquo;, até chegar num resultado aceitável.</p>
<p>Além disso, uma U-NET não é boa pra gerar imagens de altíssima resolução. O ideal é gerar imagens pequenas, 512 pixels, 1024 pixels, mas nada maior. Mas isso seria inútil pra usar profissionalmente. Pra isso servem <strong>UPSCALE MODELS</strong>. Se leu meu artigo de ontem sobre <a href="https://www.akitaonrails.com/2025/04/19/aumentando-resolucao-de-anime-velho-pra-4k-com-i-a"target="_blank" rel="noopener">Video2K</a> eu explico que escolho um modelo lá que é bom pra coerência temporal de video. Mas tinha outros modelos que são melhores pra &ldquo;IMAGENS ESTÁTICAS&rdquo;, ou fotos. E podemos usar eles no ComfyUI.</p>
<p>Depois que a nova imagem pequena é gerada pela U-NET, depois podemos usar ela como entrada pra um segundo passo de &ldquo;UPSCALE&rdquo; usando um modelo como RealERSGAN, e sair de uma imagem de 1024 pixels pra 2160 pixels, essencialmente 4K. É diferente de aumentar a imagem num Photoshop, onde fica tudo &ldquo;soft&rdquo; borrado. Como também é outro modelo de I.A. ele é treinado pra &ldquo;redesenhar&rdquo; em mais resolução. E podemos adicionar isso no workflow do ComfyUI também.</p>
<p>Mais do que isso, o ComfyUI também suporta Nodes com modelos como o HunyuanVideo da Tencent, que apresentei no outro artigo de ontem, sobre <a href="https://www.akitaonrails.com/2025/04/19/gerando-videos-de-ate-2-min-a-partir-de-uma-foto-com-i-a"target="_blank" rel="noopener">FramePack</a>. Na verdade, podemos fazer a mesma coisa que o FramePack faz mas a partir de um workflow de ComfyUI: gerar um video a partir de uma imagem, usando o mesmo modelo. O FramePack é infinitamente mais fácil de usar, mas o ComfyUI é a faca suíça de I.A.: ela faz <strong>TUDO</strong>, por isso também é infinitamente mais complicada e precisa se dedicar bastante pra aprender a usar.</p>
<p>Entendendo até aqui, olhem o workflow que eu usei (está com zoom out, dá pra aumentar o zoom dinamicamente):</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/rrtdjr0sq5vsl3xnm08w3sz459w8" alt="workflow"  loading="lazy" /></p>
<p>Dá pra ver esses passos intermediários usando Nodes de <strong>PREVIEW</strong> então é mais ou menos assim que podemos usar o ComfyUI pra fazer muita coisa avançada. Como falei, eu mesmo ainda não comecei nem a arranhar a superfície.</p>
<p>Vamos resumir alguns dos conceitos usados no ComfyUI agora:</p>
<h3><strong>1. Checkpoints (Modelos Base)</strong><span class="hx:absolute hx:-mt-20" id="1-checkpoints-modelos-base"></span>
    <a href="#1-checkpoints-modelos-base" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>O que são</strong>:<br>
São os modelos principais do Stable Diffusion (ex: SD 1.5, SDXL, Flux). Contêm toda a arquitetura do modelo:</p>
<ul>
<li>
<p>Text Encoder (CLIP)</p>
</li>
<li>
<p>Modelo de Difusão (U-Net)</p>
</li>
<li>
<p>VAE (Decoder de imagens)</p>
</li>
</ul>
<p><strong>Para que servem</strong>:<br>
Gerar imagens a partir do zero. Cada checkpoint tem um &ldquo;estilo&rdquo; diferente (realista, anime, etc).</p>
<p><strong>Exemplos</strong>:<br>
<code>flux.safetensors</code>, <code>realisticVisionV60.safetensors</code>, <code>anythingV5.safetensors</code></p>
<h3><strong>2. VAE (Autoencoders Variacionais)</strong><span class="hx:absolute hx:-mt-20" id="2-vae-autoencoders-variacionais"></span>
    <a href="#2-vae-autoencoders-variacionais" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>O que são</strong>:<br>
Componente que decodifica as imagens do espaço latente (formato que o modelo entende) para pixels visíveis.</p>
<p><strong>Para que servem</strong>:</p>
<ul>
<li>
<p>Melhorar detalhes e cores</p>
</li>
<li>
<p>Alguns checkpoints requerem VAEs específicos</p>
</li>
</ul>
<p><strong>Exemplos</strong>:<br>
<code>vae-ft-mse-840000.safetensors</code>, <code>animevae.pt</code></p>
<h3><strong>3. Text Encoders (Codificadores de Texto)</strong><span class="hx:absolute hx:-mt-20" id="3-text-encoders-codificadores-de-texto"></span>
    <a href="#3-text-encoders-codificadores-de-texto" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>O que são</strong>:<br>
Modelos que convertem seu prompt de texto em representações numéricas que a IA entende.</p>
<p><strong>Tipos comuns</strong>:</p>
<ul>
<li>
<p><strong>CLIP</strong>: Padrão para a maioria dos modelos</p>
</li>
<li>
<p><strong>OpenCLIP</strong>: Versão alternativa usada no SDXL</p>
</li>
<li>
<p><strong>T5</strong>: Modelos maiores para prompts complexos</p>
</li>
</ul>
<p><strong>Exemplos</strong>:<br>
<code>clip_vision.safetensors</code>, <code>openclip.safetensors</code></p>
<h3><strong>4. CLIP Vision</strong><span class="hx:absolute hx:-mt-20" id="4-clip-vision"></span>
    <a href="#4-clip-vision" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>O que são</strong>:<br>
Modelos especializados em análise visual (ao contrário do CLIP normal que é texto→imagem).</p>
<p><strong>Para que servem</strong>:</p>
<ul>
<li>
<p>Criar descrições de imagens existentes</p>
</li>
<li>
<p>Ferramentas como &ldquo;Image Prompt&rdquo; (usam a imagem como input)</p>
</li>
</ul>
<p><strong>Exemplo</strong>:<br>
<code>clip_vision_g.safetensors</code></p>
<h3><strong>5. Diffusion Models (Modelos de Difusão)</strong><span class="hx:absolute hx:-mt-20" id="5-diffusion-models-modelos-de-difusão"></span>
    <a href="#5-diffusion-models-modelos-de-difus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>O que são</strong>:<br>
Parte específica da arquitetura (U-Net) responsável pelo processo de difusão (adição/remoção de &ldquo;ruído&rdquo;).</p>
<p><strong>Para que servem</strong>:</p>
<ul>
<li>
<p>Treinamento de LoRAs</p>
</li>
<li>
<p>Workflows avançados de reutilização</p>
</li>
</ul>
<p><strong>Exemplo</strong>:<br>
<code>unet.safetensors</code></p>
<h3><strong>6. LoRAs (Adaptadores Leves)</strong><span class="hx:absolute hx:-mt-20" id="6-loras-adaptadores-leves"></span>
    <a href="#6-loras-adaptadores-leves" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>O que são</strong>:<br>
Pequenos arquivos que modificam o comportamento do checkpoint principal sem substituí-lo.</p>
<p><strong>Para que servem</strong>:</p>
<ul>
<li>
<p>Adicionar estilos específicos (ex: anime)</p>
</li>
<li>
<p>Criar personagens consistentes</p>
</li>
<li>
<p>Ajustar proporções corporais</p>
</li>
</ul>
<p><strong>Exemplo</strong>:<br>
<code>epiNoiseOffset.safetensors</code>, <code>add_detail.safetensors</code></p>
<h3><strong>7. ControlNet</strong><span class="hx:absolute hx:-mt-20" id="7-controlnet"></span>
    <a href="#7-controlnet" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>O que são</strong>:<br>
Modelos que impõem controle sobre a composição da imagem (poses, profundidade, bordas).</p>
<p><strong>Tipos comuns</strong>:</p>
<ul>
<li>
<p>OpenPose (poses humanas)</p>
</li>
<li>
<p>Canny (bordas)</p>
</li>
<li>
<p>Depth (profundidade)</p>
</li>
</ul>
<p><strong>Exemplo</strong>:<br>
<code>control_v11p_sd15_openpose.safetensors</code></p>
<h3><strong>8. Upscale Models (Modelos de Upscaling)</strong><span class="hx:absolute hx:-mt-20" id="8-upscale-models-modelos-de-upscaling"></span>
    <a href="#8-upscale-models-modelos-de-upscaling" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>O que são</strong>:<br>
Modelos para aumentar resolução e detalhes de imagens geradas.</p>
<p><strong>Tipos</strong>:</p>
<ul>
<li>
<p><strong>ESRGAN</strong>: Para detalhes realistas</p>
</li>
<li>
<p><strong>Anime6B</strong>: Especializado em arte anime</p>
</li>
</ul>
<p><strong>Exemplo</strong>:<br>
<code>4x_NMKD-Superscale-SP_178000_G.pth</code></p>
<h3><strong>9. Embeddings (Textual Inversions)</strong><span class="hx:absolute hx:-mt-20" id="9-embeddings-textual-inversions"></span>
    <a href="#9-embeddings-textual-inversions" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>O que são</strong>:<br>
Pequenos arquivos que adicionam conceitos novos ao vocabulário do modelo.</p>
<p><strong>Para que servem</strong>:</p>
<ul>
<li>
<p>Estilos artísticos específicos</p>
</li>
<li>
<p>Objetos personalizados</p>
</li>
<li>
<p>Correções de artefatos</p>
</li>
</ul>
<p><strong>Exemplo</strong>:<br>
<code>bad_prompt.pt</code>, <code>easynegative.safetensors</code></p>
<h3><strong>10. Style Models</strong><span class="hx:absolute hx:-mt-20" id="10-style-models"></span>
    <a href="#10-style-models" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>O que são:</strong><br>
Modelos especializados em aplicar estilos visuais ou “filmes” de arte sobre a imagem gerada, sem alterar de fato a estrutura básica do conteúdo.</p>
<p><strong>Para que servem:</strong></p>
<ul>
<li>
<p><strong>Colorização temática</strong> (ex: paleta de aquarela, sépia, croma‑key)</p>
</li>
<li>
<p><strong>Simular técnicas artísticas</strong> (óleo, guache, traço de mangá)</p>
</li>
<li>
<p><strong>Unificar estética</strong> em séries de imagens (mesmo “clima” de cor e luz)</p>
</li>
</ul>
<p><strong>Exemplo de arquivos:</strong></p>
<ul>
<li>
<p><code>flux1-redux-dev.safetensors</code> (estilo “Flux Redux”)</p>
</li>
<li>
<p><code>watercolor_v2.safetensors</code> (pinceladas de aquarela)</p>
</li>
<li>
<p><code>film_grain_vintage.safetensors</code></p>
</li>
</ul>
<h3><strong>11. SAMs (Segment Anything Models)</strong><span class="hx:absolute hx:-mt-20" id="11-sams-segment-anything-models"></span>
    <a href="#11-sams-segment-anything-models" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>O que são:</strong><br>
Redes de segmentação gerais capazes de isolar objetos ou regiões de interesse em uma imagem, produzindo máscaras binárias ou ponderadas.</p>
<p><strong>Para que servem:</strong></p>
<ul>
<li>
<p><strong>Criar máscaras de objetos/pessoas</strong> para aplicação seletiva de filtros ou composições</p>
</li>
<li>
<p><strong>Guiar ControlNets</strong> (ex: aplicar difusão apenas dentro ou fora de uma região)</p>
</li>
<li>
<p><strong>Pré‑processar referências</strong> (recortar fundo, separar camadas)</p>
</li>
</ul>
<p><strong>Exemplo de arquivo:</strong></p>
<ul>
<li><code>sam_vit_b_01ec64.pth</code> (SAM ViT‑B, modelo base de segmentação)</li>
</ul>
<h3>Resumo Visual<span class="hx:absolute hx:-mt-20" id="resumo-visual"></span>
    <a href="#resumo-visual" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>models/
├── checkpoints/        ➔ Modelos principais (Stable Diffusion)  
│     └── *.safetensors, *.ckpt  
├── diffusion_models/   ➔ Modelos de difusão alternativos  
│     └── *.safetensors, *.gguf  
├── loras/              ➔ Adaptadores de estilo (LoRAs)  
│     └── *.safetensors  
├── controlnet/         ➔ Controles de composição (ControlNets)  
│     └── *.pth, *.safetensors  
├── vae/                ➔ Decodificadores / Encoders do VAE  
│     └── *.pt, *.safetensors  
├── clip/               ➔ Encoders CLIP de texto  
│     └── *.safetensors, *.pt  
├── clip_vision/        ➔ Encoders CLIP de visão  
│     └── *.safetensors  
├── text_encoders/      ➔ Encoders de texto avançados  
│     └── *.safetensors, *.bin  
├── upscale_models/     ➔ Modelos de super‑resolução  
│     └── *.pth  
├── sams/               ➔ Modelos Segment Anything (SAM)  
│     └── *.pth, *.onnx  
├── style_models/       ➔ Modelos de estilo visual  
│     └── *.safetensors  
└── embeddings/         ➔ Embeddings / conceitos personalizados  
      └── *.pt, *.safetensors  </code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Tem que baixar as coisas certas nos lugares certos. E pra dificultar, como já falei, os arquivos costumam ter a mesma extensão. Então é muito fácil confundir e jogar um text encoder no diretório de clip ou vice-versa. De novo, por isso encorajo que atualize o arquivo <a href="https://github.com/akitaonrails/ComfyUI-Docker-CUDA-preloaded/blob/master/models.conf"target="_blank" rel="noopener">&ldquo;models.conf&rdquo;</a> e coloque as URLs certas nas listas certas, assim fica bem mais organizado e o meu Docker vai puxar as coisas pros lugares certos quando inicializar.</p>
<p>Ainda assim pode ter algumas outras dúvidas que eu também tive no começo. Por exemplo <strong>&ldquo;Por que a U-NET é chamada de Checkpoint?&rdquo;</strong></p>
<h3><strong>Checkpoints ≈ Sistema Operacional Completo</strong><span class="hx:absolute hx:-mt-20" id="checkpoints--sistema-operacional-completo"></span>
    <a href="#checkpoints--sistema-operacional-completo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>
<p><strong>Semelhança</strong>: Assim como um commit no Git captura um estado específico do código, um checkpoint é um &ldquo;snapshot&rdquo; completo de um modelo treinado em um estágio específico.</p>
</li>
<li>
<p><strong>Diferença</strong>: Checkpoints são <strong>autocontidos</strong> (não são &ldquo;diferenças&rdquo; como commits), cada um é um modelo completo que pode funcionar independentemente.</p>
</li>
<li>
<p><strong>Melhor Metáfora</strong>: Pense em checkpoints como diferentes versões do Windows (XP, 7, 10) - cada um é um sistema operacional completo, não incremental.</p>
</li>
</ul>
<p>Então checkpoints meio que se assemelham, em conceito, a um &ldquo;git commit&rdquo; - se você for desenvolvedor. Ou snapshots de BTRFS ou ZFS - se você for de infra/devops.</p>
<h3><strong>LoRAs ≈ Plugins/Extensions</strong><span class="hx:absolute hx:-mt-20" id="loras--pluginsextensions"></span>
    <a href="#loras--pluginsextensions" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><ul>
<li>
<p><strong>Semelhança</strong>: Assim como um PR adiciona funcionalidades a um código base, um LoRA modifica o comportamento do modelo base.</p>
</li>
<li>
<p><strong>Diferença</strong>: LoRAs não são &ldquo;merged&rdquo; no checkpoint original, mas sim <strong>aplicados dinamicamente</strong> durante a inferência.</p>
</li>
<li>
<p><strong>Melhor Metáfora</strong>: São como extensões do Chrome (ex: AdBlock) - adicionam comportamentos específicos sem modificar o navegador em si.</p>
</li>
</ul>
<p>Pense assim: um modelo tem Checkpoints, como se fossem commits, assim dá pra &ldquo;continuar&rdquo; o treino por cima se precisar. Ao mesmo tempo, não precisamos mexer no modelo principal, podemos criar camadas separadas, as chamadas LoRas. Por exemplo, digamos que um modelo esteja com dificuldades de gerar o seu rosto, não importe que prompt use ou que Nodes configure.</p>
<p>Em vez disso podemos usar o &ldquo;dicionário&rdquo; do modelo pré-treinado, como Flux ou SDXL e fazer um treinamento com nossas fotos pessoais e gerar um LoRa específico meu. Então podemos adicionar um Node de LoRa no workflow e isso vai resultar em imagens muito melhores com seu rosto em particular.</p>
<p>Existem comunidades online inteiras dedicadas a isso. Um exemplo. No site <a href="https://civitai.com/"target="_blank" rel="noopener">Civit A.I.</a> vamos encontrar diversos modelos baseados em modelos pré-existentes, como este <a href="https://civitai.com/models/24149/mistoonanime"target="_blank" rel="noopener">Mistoon</a> que é baseado no SDXL.</p>
<p><a href="https://civitai.com/models/24149/mistoonanime"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/a4s6zs7oq6rtwto79lqk5itgd4or" alt="Mistoon Civit.ai"  loading="lazy" /></a></p>
<p>É um checkpoint. Podemos nos cadastrar no site e e baixar o arquivo <code>mistoonAnime_v10Noobai.safetensor</code> e colocar no diretório <code>models/checkpoints</code>.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/17z9fbwbzkg5kwoxzrcivpwwd74c" alt="Mistoon Checkpoint Node"  loading="lazy" /></p>
<p>Agora, em todo workflow que tiver o Node &ldquo;Load Checkpoint&rdquo; temos a opção de escolher esse modelo. Mas digamos que esse modelo não esteja conseguindo gerar uma personagem com uniforme colegial como em vários animes. Vasculhando o site, encontramos esta <strong>LoRa</strong>:</p>
<p><a href="https://civitai.com/models/115968/mistoonanime-school-uniform"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/xlvhozw5u90xx9130a1kin5dl0da" alt="Mistoon LoRa"  loading="lazy" /></a>
O modelo Mistoon é grande, uns 6.6GB mas essa LoRa - chamada <code>Mistoon_Anime\ school\ uniform.safetensor</code> que devemos mover pra <code>models/loras</code> tem menos de 290 MB, é muito menor porque é um treinamento específico só em imagens de uniformes colegiais.</p>
<p>Agora podemos ligar o Node de &ldquo;Load Checkpoint&rdquo; a este outro Node de &ldquo;Load LoRa&rdquo; e escolher essa LoRa que sabemos que é compatível:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/8ahr2bgdxe5jg3xg4y2wcie3qzvs" alt="LoRa Node"  loading="lazy" /></p>
<p>LoRas tem que ser escolhidas pra usar com os modelos em que elas foram baseadas. Como falei antes, pense no modelo como uma &ldquo;língua&rdquo;, se fizermos um LoRa em alemão, não adianta misturar com um modelo chinês, a grosso modo. Mas essa é uma forma de fazer fine-tuning pro modelo que você gosta entregar resultados que nenhum outro consegue.</p>
<p>Um problema que eu tenho com o site Civit A.I. é que só dá pra fazer download quando está logado no site. Por isso não coloquei nenhum modelo ou lora deles no meu script de instalação automática. Infelizmente no caso deles precisa manualmente baixar os arquivos e mover pros diretórios certos, anotar em algum lugar, pra em outra máquina baixar e mover tudo de novo. É um saco que eu ainda não parei pra resolver. Se alguém quiser contribuir uma solução pra isso (sem expor cookies de login de ninguém, mandem Pull Request).</p>
<h3>Um Exemplo Real<span class="hx:absolute hx:-mt-20" id="um-exemplo-real"></span>
    <a href="#um-exemplo-real" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Seu dia a dia com Comfy, sendo um novato como eu vai ser mais ou menos assim: começa indo no Google e procurando &ldquo;best workflow image to anime ComfyUI&rdquo;. Primeiros links costumam ser alguma thread de Reddit:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/zhnvm94r6ze0279obqq9k0wd6t9c" alt="Reddit"  loading="lazy" /></p>
<p>Sub-Reddits como <a href="https://www.reddit.com/r/comfyui"target="_blank" rel="noopener">r/comfyui</a> ou <a href="https://www.reddit.com/r/StableDiffusion"target="_blank" rel="noopener">r/StableDiffusion</a> costumam ter novidades e workflows como esse. Daí vamos direto pros primeiros comentários:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/n3y2qd1vxw4uldxlrs5bjsewt9vn" alt="Reddit Comentários Links"  loading="lazy" /></p>
<p>Ou isso, ou abrimos o workflow direto no ComfyUI e vasculhamos os Nodes, um a um:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/b6hu6k4ygjpjdcq0chzv6mt1kx9w" alt="Nodes Check"  loading="lazy" /></p>
<p>Já temos talvez um problema: o Node veio pré-configurado pra carregar um LoRa chamado &ldquo;SDXL_aidma-niji_jini.safetensors&rdquo; mas o link no comentário do Reddit baixa um &ldquo;SDXL_Niji_V6_DLC_LoRa_V4.safetensors&rdquo;. Procurando especificamente pelo anterior no Google, eu não acho. Mas parece &ldquo;seguro&rdquo; usar esse outro, pelo menos os nomes são muito parecidos, ambos são derivados de SDXL então teoricamente são compatíveis pelo menos. Esta á a página desse modelo no Civit A.I., então tem que baixar manualmente e mandar pro diretório <code>models/loras</code> a mesma coisa pro Checkpoint AniToon no Node acima. Baixa manualmente e move pra <code>models/checkpoints</code>.</p>
<p><a href="https://civitai.com/models/541460/sdxlnijiv6dlclora"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/m8u6e9hc0vyrd41pdru0c26akolr" alt="LoRa Niji"  loading="lazy" /></a></p>
<p>Note que nessa página ele explica que esse LoRa foi feito pra funcionar com o modelo SDXL_Niji_V6 e não com o AniToon como o Workflow que baixei sugere no Node de Load Checkpoint. Vale testar com ambos. De qualquer forma, já que estamos no site, vamos baixar o modelo sugerido também:</p>
<p><a href="https://civitai.com/models/120765"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/zrkk8wr9v1z24s5f5fgyv01uui4g" alt="SDXL Niji"  loading="lazy" /></a></p>
<p>Arquivo <code>sdxlNijiSeven_sdxlNijiSeven.safetensors</code> de 6.5GB, move pra <code>models/checkpoints</code>, tão entendendo o processo? Baixa, move pro lugar certo, recarrega o workflow no navegador e agora aparece, selecionamos:</p>
<p>Aliás, acho que é intuitivo mas pra quem não entendeu se abriu a interface do ComfyUI:</p>
<ul>
<li>botão esquerdo do mouse seleciona Nodes</li>
<li>botão do meio arrasta a tela</li>
<li>scroll faz zoom na interface</li>
</ul>
<p>Agora sim, vamos escolher esses dois pra testar:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/m7bzo57hfez23vazar5ct4j5kndq" alt="Nodes Niji"  loading="lazy" /></p>
<p>Aproveitando essa mesma imagem, note no canto esquerdo um Node de Florence2 que é uma LLM da Microsoft (e meu script de Docker já instala pra você). Não dá pra mostrar todos os Nodes relacionados mas estes são os principais de pra que isso serve:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/8pjx9pr8x0jsmz7bhouz5zcft1zt" alt="Florence"  loading="lazy" /></p>
<p>O ideal em geração de imagem é fazer o prompt mais longo e bem detalhado sobre a imagem original que você conseguir, mas a maioria das pessoas só escreve alguma porcaria como &ldquo;pessoa sorrindo de pé&rdquo; e acha que é suficiente. O modelo Florence2 é feito pra ler uma imagem e descrever ela em texto.</p>
<p>Agora tem outro detalhe: muitos checkpoints ou Loras são treinados com alguma palavra-chave de ativação. Assim ela só influencia se você digitar essas palavras no seu prompt. Então esse workflow não precisa digitar prompt manualmente, o Florence2 vai fazer isso e toda vez o texto dele começa como &ldquo;The image &hellip; bla bla bla&rdquo;, daí temos um Node de &ldquo;Text Find and Replace&rdquo; que troca esse &ldquo;The Image&rdquo; pelas palavras chaves do Anitoon ou Niji: &ldquo;aidma-niji, niji&rdquo;.</p>
<p>Essas são as palavras certas? <strong>EU NÃO SEI</strong> kkkkk</p>
<p>O workflow já trouxe essas palavras, mas nas páginas do Civit A.I. - onde deveria estar documentado, não está!! Então estamos realmente <strong>CHUTANDO</strong> que deve ser &ldquo;niji&rdquo; já que é o nome do modelo e do lora. Preste atenção na palavra-chave, é importante, senão você adiciona o lora e nada acontece de diferente e é porque faltou isso no prompt!!</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/4i1n0fc3uvvy85nr17x6imee33bh" alt="ControlNet"  loading="lazy" /></p>
<p>Tem mais, tem que checar se os Nodes de coisas como VAE, ControlNet, Canny e tudo mais estão populados com arquivos que temos. Normalmente se usa a mesma meia dúzia e eu já pré-instalei no Docker os principais. Na imagem, veja que ControlNet é o <code>contronet-union-sdxl-1.0-promax.safetensors</code>, compatível com SDXL e estamos usando Niji que é feito em cima de SDXL. Vamos chutando assim.</p>
<p>Também notem que ele usa Zoe Depth Map pra tirar o Mapa de Profundidade e Canny pra tirar o Mapa de Bordas como no outro workflow. Dá pra configurar, nesse workflow note que o Mapa de Bordas pegou alguns artefatos esquisitos no fundo, dá pra ajustar pra tirar isso. Por isso que no exemplo anterior também a imagem final apareceu uma janela, o modelo achou que era uma janela! Mas por enquanto deixa assim mesmo.</p>
<p>Agora podemos rodar:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/29nzuo5t1nr6mn4wss0t77at94no" alt="Run Comfy"  loading="lazy" /></p>
<p>Tem um botão enorme &ldquo;RUN&rdquo; em azul lá embaixo. Se estiver tudo configurado certo, você vai ver que um trabalho foi colocado na fila. A interface é inteligente o suficiente pra deixar você trabalhar em outros workflow e só ir enfileirando trabalhos enquanto a GPU sua processando. Não precisa ficar esperando, só deixar vários enfileirados e ir dormir.</p>
<p>Ao rodar, na interface os Nodes que estão processando neste momento vão ficar com uma borda verde, o que é bom pra você saber em que etapa do processo ele está. E se algum Node tiver erro - porque faltou arquivo, por exemplo - ele vai ficar com borda vermelha, daí precisa corrigir e rodar de novo.</p>
<p>E no final, eis um dos resultados:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/7mbzamhj8c5sc2fesp8kkz6cvjhu" alt="Resultado Niji"  loading="lazy" /></p>
<p>Note como os mapas da controlnet garantiram a pose correta. O modelo deduziu aqueles artefatos de fundo do mapa de borda como iluminação de alguma janela, mas na foto original vemos que é tipo o tijolo de concreto da parede kkkk mas é isso, sem saber, ele chuta. E se rodar várias vezes, sempre vai dar um resultado bem diferente. Olha outro:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/xbxu131ivlqu4endtdf6j5ayxzxt" alt="Niji 2"  loading="lazy" /></p>
<p>Nada a ver kkkk Mas podemos trocar o modelo SDXL-Niji pelo Anitoon. Vamos ver um dos resultados:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/w7fsjrui29pbp8e98ka4ubuvtl6f" alt="Anitoon"  loading="lazy" /></p>
<p>Trocando pro modelo waiNSFIllustrious:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2un0xtv7g210xrqc74nbceiwg7t4" alt="waiNSFIllustrious"  loading="lazy" /></p>
<p>Esse ficou ainda mais nada a ver. Alguma coisa nos mapas está fazendo o modelo assumir que eu tenho cabelo cacheado, sei lá porque. Mas é isso: tem que ajustar os parâmetros. O resultado do workflow que mostrei no começo do artigo deu um resultado melhor, usando os mesmos modelos.</p>
<p>De todos os Nodes, um dos mais importante é o &ldquo;motor&rdquo; do processo, o <strong>KSampler</strong>:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/n6875915bhqq39vxw0kfripyyj3d" alt="KSampler"  loading="lazy" /></p>
<p>Esse é o cara que comanda o show. Damos pra ele o modelo, o prompt, saídas de control-net e ele quem vai iterativamente (steps) fazer o &ldquo;desnoise&rdquo; do Latente. Note que sempre existe um fator aleatório (&ldquo;seed&rdquo;), quantidade de passos (25 a 50 é a média), &ldquo;cfg&rdquo; (classifier-free guidance) ou &ldquo;guidance_scale&rdquo;, onde valores maiores puxam a imagem mais seu prompt positivo. Denoise é quando de noise o scheduler aplica (1.0 é noise completo, abaixo de 1.0 pode produzir resultados mais &ldquo;artísticos&rdquo;, tem que testar).</p>
<p>Sampler Name é um saco porque precisaria entender a matemática por trás, mas são os algoritmos de steps, por exemplo <code>euler</code>, <code>ddim</code>, <code>plms</code> e cada um tem um trade-off entre velocidade e fidelidade. Na prática: teste um a um e veja os resultados. Scheduler tem <code>normal</code>, <code>klms</code>, <code>dpmsolver</code>. Mesma coisa: teste um a um.</p>
<p>Como podem ver, tem BASTANTE parâmetros que dá pra brincar. Mas o fluxo principal é mais ou menos esse. Daí tem que ir aprendendo Node a Node o que cada um pode fazer e baixar bastante workflow pré-pronto pra ver como o pessoal tem solucionado diversos problema, quais os modelos e loras mais populares pra cada tipo de aplicação. Enfim, o Reddit é seu amigo. Espero que tenha dado pra dar um Norte, eu também ainda estou aprendendo.</p>
]]></content:encoded><category>midia-generativa</category><category>machine-learning</category><category>tutoriais</category></item><item><title>Gerando Imagens com I.A - até estilo Ghibli 😂 - com Docker e CUDA</title><link>https://akitaonrails.github.io/2025/04/20/gerando-imagens-com-i-a-ate-estilo-ghibli-com-docker-e-cuda/</link><guid isPermaLink="true">https://akitaonrails.github.io/2025/04/20/gerando-imagens-com-i-a-ate-estilo-ghibli-com-docker-e-cuda/</guid><pubDate>Sun, 20 Apr 2025 17:30:00 -0300</pubDate><description>&lt;p&gt;Este tema vai ser dividido em dois posts. Neste é só técnico de como fazer rodar, no outro vou explicar o que diabos é um ComfyUI e mais ou menos como usar.&lt;/p&gt;
&lt;p&gt;Mais de ano atrás, quando fiz meus últimos videos sobre I.A. e nos podcasts eu sempre falava que rodava tudo na minha máquina. Mas nunca detahei como. Então hoje consertei isso com vários posts de blog e projetinhos no GitHub com Dockerfiles pra você mesmo rodar na sua própria máquina, sem configurar nada difícil.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Este tema vai ser dividido em dois posts. Neste é só técnico de como fazer rodar, no outro vou explicar o que diabos é um ComfyUI e mais ou menos como usar.</p>
<p>Mais de ano atrás, quando fiz meus últimos videos sobre I.A. e nos podcasts eu sempre falava que rodava tudo na minha máquina. Mas nunca detahei como. Então hoje consertei isso com vários posts de blog e projetinhos no GitHub com Dockerfiles pra você mesmo rodar na sua própria máquina, sem configurar nada difícil.</p>
<p>Finalmente fiz um <a href="https://github.com/akitaonrails/ComfyUI-Docker-CUDA-preloaded"target="_blank" rel="noopener">Dockerfile</a> pra ComfyUI, que é a melhor interface gráfica pra edição de workflows de geração de imagens. Se você já trabalhou com Davinci Resolve Fusion ou Blender Geometry Nodes da vida, workflows são basicamente configurações de nós pra processar suas imagens, áudio, video e ir passando por processamentos não só de I.A.. É como programar visualmente. Por isso é uma ferramenta muito mais complicada do que o FramePack que mostrei no post anterior. Não é só clicar.</p>
<p>Recentemente todo mundo ficou histérico com as atualizações do ChatGPT e como ele <em>&ldquo;OH MY GOD&rdquo;</em> consegue gerar imagens em estilo de anime da Ghibli e ficaram spammando o mundo com memes idiotas em estilo anime. Histeria online é um saco mesmo. Mas como eu falei, por mais impressionante que seja, é isso: um gerador de memes. Não dá pra usar profissionalmente.</p>
<p>Cansei de tentar subir fotos minhas ou até fotos genéricas, de personagens fictícios, alguns até desenhos meus, e vira e mexe, aleatoriamente, o ChatGPT se recusa a gerar a imagem. Alegando privacidade, segurança, copyright e tudo mais que ele conseguir inventar de desculpa. Não dá pra confiar nisso pra trabalhar. Quem é profissional deveria ter controle completo sobre o processo e é isso que vou ensinar hoje.</p>
<p>Pra começar, clone meu <a href="https://github.com/akitaonrails/ComfyUI-Docker-CUDA-preloaded"target="_blank" rel="noopener">repositório no GitHub</a> e siga as instruções do README ou faça isto:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>git clone https://github.com/akitaonrails/ComfyUI-Docker-CUDA-preloaded.git
cd ComfyUI-Docker-CUDA-preloaded
docker compose build</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Sendo mais complexo, precisei colocar um Docker Compose desta vez. Então pra subir, depois do build finalizar, só fazer isso:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>docker compose up</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora vai demorar, porque por padrão ele vai baixar quase <strong>500GB</strong> de modelos pré-treinados pra conseguir fazer quase tudo de imagens. Sim, pra brincar com I.A. localmente precisa ter infra. Ou se não, alugar máquina parruda com muito armazenamento numa RunPod da vida e rodar remoto. Tem como baixar menos modelos, vou explicar a seguir.</p>
<p>No Docker Compose. Recomendo evitar a opção &ldquo;-d&rdquo; de subir como daemon, porque é bom reservar um terminal pra ficar vendo os outputs (você pode dar <code>docker compose logs --follow</code>, se quiser também, mas é mais fácil direto).</p>
<p>O ComfyUI é um programa mais complexo que a média, muito mais que um FramePack da vida. Ele tem suporte a instalar &ldquo;extensions&rdquo; (plugins, addons, chame como quiser). E na verdade ele sozinho não faz muita coisa, precisa instalar várias extensions pra começar a ficar realmente útil. Vira e mexe alguma dessas extensions não trata erros direito e estoura no console, algo como isso:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/n7w5oxun51elrsv2fra46f684xre" alt="Console Error"  loading="lazy" /></p>
<p>É útil sempre ficar de olho no console pra descobrir se ele tentou carregar um arquivo que não existe. Daí dá pra pegar o nome do arquivo e buscar no Google, baixar e colocar no lugar certo. Ou se é uma extension muito bugada que é melhor tirar do diretório &ldquo;custom_nodes&rdquo;, e assim por diante. Sem ver o console, não aparece na interface web e você vai ficar a ver navios.</p>
<p>Muito dos modelos e extensions eu me baseei na configuraçãol <strong>Ultimate ComfyUI versão 3</strong> recomendada pelo <strong>Aitrepreneur</strong>, de novo. Assista <a href="https://youtu.be/q5kpr84uyzc"target="_blank" rel="noopener">este video dele</a> pra ter todos os detalhes e tudo que é possível fazer. Ele tem os scripts pra Linux, Windows, e os workflow na versão paga do Patreon dele e, claro, não vou compartilhar os workflows porque não são meus. Em teoria, minha config deve ser capaz de fazer o que ele ensina.</p>
<p>O workflow V3 do Aitrepreneur é muito ph0da. Mas eu não posso compartilhar porque é material fechado no Patreon dele, mas eis uma foto de tela do meu ComfyUI com tudo dele carregado:</p>
<p><a href="https://www.patreon.com/posts/ultimate-all-in-121355089"target="_blank" rel="noopener"><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/1p3xfs0ecfimbw4yg55qz17buti6" alt="Ultimate V3"  loading="lazy" /></a></p>
<p>Mas a config em si, eu refiz inteira pra funcionar neste Docker. Dá pra carregar qualquer outro workflow aberto que achar num Reddit da vida.</p>
<p>O que eu fiz foi o seguinte:</p>
<h2>Models<span class="hx:absolute hx:-mt-20" id="models"></span>
    <a href="#models" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>No projeto tem que ter um diretório &ldquo;models&rdquo;, se não tiver crie, porque o docker compose vai mapear pra dentro do container. Quando iniciar, ele vai rodar o arquivo <a href="https://github.com/akitaonrails/ComfyUI-Docker-CUDA-preloaded/blob/master/init_models.sh"target="_blank" rel="noopener">init_models.sh</a>. Esse script vai carregar um arquivo de configuração que você pode editar depois: <a href="https://github.com/akitaonrails/ComfyUI-Docker-CUDA-preloaded/blob/master/models.conf"target="_blank" rel="noopener">models.conf</a>. Ele é enorme e um pedaço dele é assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>...
[CONTROLNET]
https://huggingface.co/XLabs-AI/flux-controlnet-collections/resolve/main/flux-canny-controlnet-v3.safetensors
https://huggingface.co/XLabs-AI/flux-controlnet-collections/resolve/main/flux-depth-controlnet-v3.safetensors
https://huggingface.co/brad-twinkl/controlnet-union-sdxl-1.0-promax/resolve/main/diffusion_pytorch_model.safetensors|controlnet-union-sdxl-1.0-promax.safetensors
https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_canny.pth
https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11f1p_sd15_depth.pth
https://huggingface.co/lllyasviel/ControlNet-v1-1/resolve/main/control_v11p_sd15_openpose.pth
https://huggingface.co/Aitrepreneur/FLX/resolve/main/Shakker-LabsFLUX1-dev-ControlNet-Union-Pro.safetensors

[STYLE_MODELS]
https://huggingface.co/Aitrepreneur/FLX/resolve/main/flux1-redux-dev.safetensors

[SAMS]
https://huggingface.co/Aitrepreneur/FLX/resolve/main/sam_vit_b_01ec64.pth</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Cada seção mapeia pra um sub-diretório, então &ldquo;[SAMS]&rdquo; vai mapear pra &ldquo;models/sams&rdquo; e baixar os binários nos lugares certos. Tem alguns especiais nas seções &ldquo;[GIT_REPOS]&rdquo;, para os que podem ser clonados via git ou &ldquo;[CUSTOM]&rdquo; pra arquivos que precisa baixar depois dentro desses projetos clonados, por exemplo. Não sei ainda se dá pra simplificar, mas isso eu peguei na config do Aitrepreneur.</p>
<p>O ideal é sempre manter essa lista organizada com modelos que acharem depois. Se quiserem contribuir também seria bacana. O motivo é que fica tudo espalhado em tudo que é site por aí, você baixa manualmente, daí move pro diretório dentro de &ldquo;models&rdquo;. Amanhã, se reinstalar sua máquina, e pra lembrar todos os sites? São dezenas de modelos disponíveis.</p>
<p>No outro post vou tentar resumir o que diabos são &ldquo;checkpoints&rdquo;, &ldquo;diffusion models&rdquo;, &ldquo;loras&rdquo;, &ldquo;controlnet&rdquo; e tudo mais. Mas por enquanto, a extensão dos arquivos não diz muita coisa. Dizer &ldquo;.pt&rdquo; por exemplo só quer dizer que é um arquivo que o PyTorch consegue abrir, mas não diz se é um clip ou um text encoder, por exemplo. Extensão &ldquo;.safetensors&rdquo; mesma coisa, pode ser qualquer coisa, e o ComfyUI precisa que cada arquivo esteja no sub-diretório correto pra conseguir usar depois, não pode sair movendo arquivo aleatoriamente.</p>
<p>O script &ldquo;init_models.sh&rdquo; roda toda vez que o Docker é reiniciado e é inteligente pra só baixar URLs que já não estão baixadas no diretório &ldquo;models&rdquo;. Por isso sempre roda quando o container sobe, no entrypoint. Essa é uma das vantagens da minha config: ela é tem manutenção automática.</p>
<h3>Extensions e Dependências<span class="hx:absolute hx:-mt-20" id="extensions-e-dependências"></span>
    <a href="#extensions-e-depend%c3%aancias" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Eu fiz um segundo script chamado <a href="https://github.com/akitaonrails/ComfyUI-Docker-CUDA-preloaded/blob/master/init_extensions.sh"target="_blank" rel="noopener">init_extensions.sh</a>, que também roda no entrypoint do container e você não deve mexer, mas ele carrega um outro arquivo de configuração chamado <a href="https://github.com/akitaonrails/ComfyUI-Docker-CUDA-preloaded/blob/master/extensions.conf"target="_blank" rel="noopener">extensions.conf</a> e, esse sim, você pode mexer e adicionar novas extensions depois. Um pedaço dele é assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>[EXTENSIONS]
https://github.com/ltdrdata/ComfyUI-Manager.git
https://codeberg.org/Gourieff/comfyui-reactor-node.git
https://github.com/ssitu/ComfyUI_UltimateSDUpscale.git
https://github.com/kijai/ComfyUI-LivePortraitKJ.git
https://github.com/PowerHouseMan/ComfyUI-AdvancedLivePortrait.git
...</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Aqui eu declaro todas as extensions que adicionam novas funcionalidades em cima do ComfyUI, em particular novos <strong>NODES</strong>, por isso ficam no diretório &ldquo;custom_nodes&rdquo;, que é um volume externo ao Docker e por isso é persistente, ou seja, toda nova extension sobrevive restarts do container.</p>
<p>Mas tem ainda um porém GRANDE: <strong>Dependências de Python</strong>. Um pesadelo.</p>
<p>A build da Imagem Docker faz um monte de <code>pip install</code> e como estou usando Virtual ENV, vai tudo instalado em <code>/venv</code> dentro do container, como deveria.</p>
<p>Porém, as extensions que são instaladas fora da build - toda vez que o container reinicia - costumam ser código Python e também tem mais dependências. Extensions são projetos no GitHub e podem estar mudando neste exato momento. Se mudar e fizer um <code>git pull</code> pra atualizar, precisa rodar o <code>pip install -r requirements.txt</code>. Mas se tiver dezenas de extensions, rodar <code>pip install</code>, de cada um, em cada restart do Docker é uma demora grande pra não fazer quase nada. Como eu disse, pesadelo.</p>
<p>Minha solução - pra me gabar um pouco - conserta boa parte dessa manutenção. Quando o script <code>init_extensions.sh</code> executa, se uma extension da lista já estiver instalada, ele vai pular. Daí vai gravar no diretório <code>./custom_nodes/.last_commit/*.commit</code> o último commit de cada extension. Quando o Docker reiniciar, ele vai dar um <code>git fetch</code> e checar se o hash do último commit mudou.</p>
<p>Só se mudou, ele vai rodar <code>pip install</code> pra essa extension que ganhou atualização e pular todas as outras que não mudaram desde o último restart, economizando MUITO tempo e evitando que você precisa manualmente atualizar as coisas. Como estou usando VENV, vai ficar tudo persistido no volume &ldquo;/venv&rdquo;. Sim, é um VOLUME, porque ela muda depois do build a cada atualização de extension.</p>
<p>Dessa forma, tudo que tem na imagem vai ser copiada nesse volume, e tudo que as extensions instalarem por fora, também vai pra dentro desse volume. Toda vez que reiniciar, volta tudo como estava antes. Foi a solução que encontrei e até agora parece funcionar direitinho.</p>
<p>Mas é só questão de tempo pra alguma extension mal feita instalar dependências que quebrem outras dependências, ou um caso real que eu passei: tinha uma extension que crasheia sem mensagem de erro se estiver usando Python 3.11, o Zoe Depth Map da extension <code>comfyui_controlnet_aux</code>, um dos pacotes que parece que são populares.</p>
<p>Eu não tinha me dado conta que comecei usando uma image base de Docker de ubuntu22 em vez de ubuntu24, eu estava com medo de usar algo novo demais e quebrar, mas acabei usando velha demais e quebrou do mesmo jeito. Gerenciar versionamento é um saco. Enfim, mudei o Dockerfile pra ubuntu24.</p>
<p>Mas no volume externo do VENV agora sobrou um monte de dependências que se limitavam ao Python 3.11. Não tem problema, basta apagar o volume:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>docker compose down # garantir que está parado
docker volume rm comfyui-venv
rm ./custom_nodes/.last_commits/*.commit</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E pronto, o VENV vai reiniciar vazio, preencher com as dependências corretas de Python 3.12 da imagem e ao iniciar o container meu script <code>init_extensions.sh</code> não vai achar o &ldquo;.last_commits&rdquo; e vai forçar um <code>pip install -r requirements.txt</code> de todas as extensions, agora com Python 3.12. Super limpo e confiável.</p>
<p>Se mesmo assim tiver erro, aí pode ser bug numa extension em especial, só vá até o diretório dele em &ldquo;./custom_nodes&rdquo; e apague manualmente antes de reiniciar o container.</p>
<p>Acho que isso é o menos complicado que dá pra fazer nesse caso. Esse docker não foi feito pra rodar sozinho num servidor, é pra rodar na sua própria máquina local, onde você está monitorando o output do terminal mesmo.</p>
<p>Ah sim, se sair versão nova do ComfyUI aí tem que re-buildar a imagem e apagar o volume de VENV. A imagem é estática, não esqueçam. Dentro o Dockerfile faz um novo git clone e pega o commit mais novo. Daí vai instalar pacotes python mais novos, então precisa zerar o volume da VENV e fazer as extensions re-instalarem suas dependências depois também. É um enorme saco, mas é assim mesmo que funciona.</p>
<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Feito tudo isso, com um simples <code>docker compose up</code> consigo ter um ambiente de ComfyUI estável e isolado do resto do meu sistema, sem sujar nada com milhares de dependências duvidosas. Quando terminar só fazer <code>docker compose down</code> ou &ldquo;ctrl-c&rdquo; no console dele e pronto, meu sistema continua limpo.</p>
<p>Além disso, diretórios como &ldquo;models&rdquo; e seus 500GB de modelos ficam separados, dá pra fazer backup pra outro lugar se precisar. E se quiser subir em outra máquina, basta levar somente esses diretórios e subir Docker Compose lá e tem que subir igualzinho, zero-setup. Esse foi meu objetivo ao Dockerzar ComfyUI, FramePack, Video2K, ter tudo portátil, isolado e limpo pra subir em qualquer máquina depois sem precisar ficar rodando scripts duvidosos de instalação direto na minha máquina.</p>
<p><a href="https://x.com/AkitaOnRails/status/1914030815962411367"target="_blank" rel="noopener">No meu X</a> eu já mostrei meu ComfyUI funcionando. Carreguei um workflow qualquer que achei na Web e olha só:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/jhx6v5va7qac4vocp17ku5o6ha51" alt="ComfyUI"  loading="lazy" /></p>
<p>Nesse workflow eu subo uma imagem de referência e posso escrever um prompt de como quero transformar ela:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/20xq72nojtvb887mebeksr6q32cw" alt="Prompt"  loading="lazy" /></p>
<p>Com ComfyUI eu tenho controle sobre todas as etapas do processo, por exemplo, esse workflow consegue separar coisas como Mapa de Profundidade e Contorno de Bordas da imagem original, pra usar de contexto antes de fazer a imagem final, garantindo um resultado muito mais preciso:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/4r1en3a557znzmy0zojk8ei9tflu" alt="Depth Map"  loading="lazy" /></p>
<p>E eis um resultado que já consegui usando o modelo &ldquo;waiNSFIllustrious&rdquo; que esse workflow sugere, que é um modelo treinado pra gerar imagens estilo anime (tem dezenas, pra vários estilos diferentes).</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/m7amy8gmp5y4snztatjjlwlr0wye" alt="anime"  loading="lazy" /></p>
<p>Mudando o modelo pra <code>Mistoon</code>, baseado em Stable Diffusion (SDXL), eis outro resultado, com o mesmo prompt, no mesmo workflow:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/lkj86wdlvh5fy8h78a8ml5io8t6g" alt="anime 2"  loading="lazy" /></p>
<p>E posso ficar tunando dezenas de parâmetros dentro desse workflow, trocar vaes, text encodes, controlnets e muito mais até conseguir exatamente o resultado que eu quiser. Esse é o poder do ComfyUI e seus Custom Nodes e Workflows. É o que vou tentar explicar o básico no próximo post.</p>
]]></content:encoded><category>midia-generativa</category><category>containers</category><category>tutoriais</category></item><item><title>Colorindo Imagens Preto e Branco com I.A.</title><link>https://akitaonrails.github.io/2025/04/19/colorindo-imagens-preto-e-branco-com-i-a/</link><guid isPermaLink="true">https://akitaonrails.github.io/2025/04/19/colorindo-imagens-preto-e-branco-com-i-a/</guid><pubDate>Sat, 19 Apr 2025 01:40:00 -0300</pubDate><description>&lt;p&gt;Minha namorada me deu um desafio hoje: ela tinha fotos preto e branco antigas e queria saber se eu conseguia colorizar elas. Se sair procurando na Web esbarra em alguns sites pra isso, como esse: &lt;a href="https://palette.fm/"target="_blank" rel="noopener"&gt;Palette&lt;/a&gt;. Mas é pago, não é barato e acho que ele não consegue uma coisa que ela queria: usar uma outra imagem colorida como referência pra tirar as cores em vez de tentar colorizar por chute do modelo.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Minha namorada me deu um desafio hoje: ela tinha fotos preto e branco antigas e queria saber se eu conseguia colorizar elas. Se sair procurando na Web esbarra em alguns sites pra isso, como esse: <a href="https://palette.fm/"target="_blank" rel="noopener">Palette</a>. Mas é pago, não é barato e acho que ele não consegue uma coisa que ela queria: usar uma outra imagem colorida como referência pra tirar as cores em vez de tentar colorizar por chute do modelo.</p>
<p>Saí fuçando GitHub e tem uma página <a href="https://github.com/MarkMoHR/Awesome-Image-Colorization"target="_blank" rel="noopener">Awesome Image Colorization</a> com vários papers de pesquisa. Muito útil pra quem for pesquisador mas totalmente inútil pra mim que não vou fazer um do zero kkkk. Tinha links pra alguns projetos como ChromaGAN, mas que está descontinuado faz uns 5 anos. E vários outros projetos que eu vi foram descontinuados lá atrás mesmo, não sei porque tem tão pouco open source ainda atualizado.</p>
<p>Mas felizmente esbarrei em um promissor: <a href="https://github.com/piddnad/DDColor"target="_blank" rel="noopener">DDColor</a>.</p>
<p>Bora clonar o repositório. E pra continuar, prefiro fazer tudo dentro de Docker. Ficar baixando dependência de python sempre zoa meu sistema e deixa um monte de lixo pra trás, então melhor coisa é isolar tudo. Outra coisa, precisa baixar os binários dos modelos pré-treinados. Só pra isso vou usar um pouco de Python por causa da lib &ldquo;modelscope&rdquo; que faz isso:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git clone https://github.com/piddnad/DDColor.git
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> DDColor
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">mkdir modelscope
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># criar um novo venv dentro do projeto DDColor</span>
</span></span><span class="line"><span class="cl">python -m venv venv
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">venv/bin/pip3 install modelscope
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">venv/bin/python3 -c <span class="s2">&#34;from modelscope.hub.snapshot_download import snapshot_download; snapshot_download(&#39;damo/cv_ddcolor_image-colorization&#39;, cache_dir=&#39;./modelscope&#39;)&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Boa prática de Python acho que é fazer tudo dentro de uma VENV né? Enfim, isso deve baixar o binário do modelo, que pesa quase 900MB. Não é grande.</p>
<p>Agora precisamos de um Dockerfile:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code># Use NVIDIA CUDA base image with cuDNN 8 and Python support
FROM nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04

# Install Python and system dependencies
RUN apt-get update &amp;&amp; apt-get install -y --no-install-recommends \
    python3 python3-pip git libgl1 libglib2.0-0 &amp;&amp; \
    apt-get clean &amp;&amp; rm -rf /var/lib/apt/lists/*

# Set working directory
WORKDIR /app

# Copy DDColor source code into the image
COPY . /app

# Install PyTorch (CUDA 11.8 compatible) and matching torchvision/torchaudio
RUN pip3 install torch==2.2.0 torchvision==0.17.0 torchaudio==2.2.0 \
    --index-url https://download.pytorch.org/whl/cu118

# Install Python dependencies, skipping dlib (not needed for inference)
RUN sed &#39;/dlib/d&#39; requirements.txt &gt; temp-req.txt &amp;&amp; pip3 install -r temp-req.txt &amp;&amp; rm temp-req.txt

# Optional: install modelscope if you want to run snapshot_download manually inside the container
RUN pip3 install modelscope</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Preguiça de abrir um pull request pra isso, se alguém quiser, mande lá.</p>
<p>Agora precisamos colocar uma imagem preto e branco em algum lugar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>mkdir input
mkdir results
mv ~/Downloads/bw.jpg input/</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Como as fotos da minha namorada são familiares e particulares, obviamente não vou compartilhar, então peguei uma aleatória qualquer no Google Images:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/qgt3lynkp1f5uzkbysyvio2kmq04" alt="Foto B&W"  loading="lazy" /></p>
<p>Agora colocamos em &ldquo;./input&rdquo; e rodamos o Docker com este comando:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>docker run --rm --gpus all \
  -v &#34;$PWD/input&#34;:/workspace/input_images:ro \
  -v &#34;$PWD/results&#34;:/workspace/results:rw \
  -v &#34;$PWD/modelscope&#34;:/app/modelscope:ro \
  ddcolor:latest \
  python3 infer.py \
    --model_path /app/modelscope/damo/cv_ddcolor_image-colorization/pytorch_model.pt \
    --input /workspace/input_images \
    --output /workspace/results</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Preste atenção no mapeamento de diretórios. E note a opção &ldquo;&ndash;gpus&rdquo; que só funciona no meu caso porque eu tenho uma RTX 4090 instalada aqui. Não tenho a mínima idéia se é a mesma coisa com AMD, mas como a imagem é baseada em cnDNN/CUDA, acho que não. Pergunte ao ChatGPT.</p>
<p>Se estiver tudo certo e rodar com sucesso, vai levar só 1 segundo e ter este output no terminal:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>==========
== CUDA ==
==========

CUDA Version 11.8.0

Container image Copyright (c) 2016-2023, NVIDIA CORPORATION &amp; AFFILIATES. All rights reserved.

This container image and its contents are governed by the NVIDIA Deep Learning Container License.
By pulling and using the container, you accept the terms and conditions of this license:
https://developer.nvidia.com/ngc/nvidia-deep-learning-container-license

A copy of this license is made available in this container at /NGC-DL-CONTAINER-LICENSE for your convenience.

Output path: /workspace/results
100%|██████████| 1/1 [00:00&lt;00:00,  4.09it/s]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E eis o resultado que aparece no diretório `./results&quot;:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/o2r085pzzpsavbyr39btg1vq0hwp" alt="Foto re-colorizada"  loading="lazy" /></p>
<p>Sim, eu fiquei impressionado. Mesmo a imagem sendo da Web, com marca d&rsquo;agua e tudo, ele fez tudo direitinho e funcionou!!</p>
<p>Único problema é que ainda falta o problema de conseguir usar uma imagem de referência pra tentar pegar as cores. Esse é um problema mais difícil e eu perguntei ao ChatGPT que me respondeu isto:</p>
<p><strong>Sem suporte nativo para imagem de referência:</strong></p>
<p>O modelo DDColor, tanto na versão oficial hospedada no ModelScope quanto no repositório GitHub <code>piddnad/DDColor</code>, foi projetado apenas para colorização <strong>automática de imagens em preto e branco</strong>, sem aceitar uma imagem de referência como entrada. A própria descrição oficial deixa claro que o modelo recebe <strong>uma única imagem P&amp;B</strong> e retorna uma imagem colorida.</p>
<p>O método <code>forward</code> do modelo espera <strong>apenas um tensor</strong>, referente à imagem P&amp;B convertida em RGB artificial (na verdade, a luminância expandida para 3 canais). Ou seja, <strong>não há lugar no modelo para passar uma segunda imagem como referência de cores</strong>.</p>
<p>Isso também fica evidente no <code>predict.py</code> original da biblioteca Cog, que aceita apenas o parâmetro <code>image</code> como entrada — sem suporte a <code>reference</code>, <code>ref_image</code> ou algo do tipo.</p>
<h3>Como resolver o problema: duas abordagens possíveis<span class="hx:absolute hx:-mt-20" id="como-resolver-o-problema-duas-abordagens-possíveis"></span>
    <a href="#como-resolver-o-problema-duas-abordagens-poss%c3%adveis" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Como o DDColor não suporta nativamente uma imagem de referência para guiar as cores, temos duas alternativas:</p>
<h3>🔁 1. Usar um modelo especializado em colorização com referência<span class="hx:absolute hx:-mt-20" id="-1-usar-um-modelo-especializado-em-colorização-com-referência"></span>
    <a href="#-1-usar-um-modelo-especializado-em-coloriza%c3%a7%c3%a3o-com-refer%c3%aancia" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Existem modelos desenvolvidos especificamente para isso, como:</p>
<ul>
<li>
<p><strong>Deep Exemplar-based Colorization</strong> (Zhang et al., CVPR 2018)</p>
</li>
<li>
<p><strong>ChromaGAN</strong> (2019)</p>
</li>
<li>
<p><strong>Exemplar-Transformer Colorization</strong> (Yin et al., ACM MM 2021)</p>
</li>
<li>
<p><strong>UniColor</strong> (Huang et al., 2022)</p>
</li>
</ul>
<p>Esses modelos aceitam <strong>duas imagens</strong>:</p>
<ul>
<li>
<p>A imagem em P&amp;B (entrada principal)</p>
</li>
<li>
<p>Uma imagem colorida como <strong>referência de cores</strong></p>
</li>
</ul>
<p>Esses modelos mapeiam similaridades semânticas (ex: “pele aqui deve ter a mesma cor de pele dali”) e fazem uma transferência <strong>baseada no conteúdo da imagem</strong>, obtendo resultados muito mais detalhados e específicos. No entanto, isso exigiria:</p>
<ul>
<li>
<p>Baixar pesos diferentes</p>
</li>
<li>
<p>Alterar completamente a arquitetura usada</p>
</li>
<li>
<p>Adaptar o pipeline e a inferência</p>
</li>
</ul>
<h3>🎯 2. Usar transferência global de cor após a saída do DDColor (nossa abordagem atual)<span class="hx:absolute hx:-mt-20" id="-2-usar-transferência-global-de-cor-após-a-saída-do-ddcolor-nossa-abordagem-atual"></span>
    <a href="#-2-usar-transfer%c3%aancia-global-de-cor-ap%c3%b3s-a-sa%c3%adda-do-ddcolor-nossa-abordagem-atual" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A solução prática e leve que usamos foi:<br>
➡️ Rodar o DDColor normalmente para gerar a imagem colorida automaticamente<br>
➡️ Depois, ajustar <strong>globalmente</strong> as cores do resultado para ficarem mais parecidas com as da imagem de referência.</p>
<p>Esse ajuste é feito com o algoritmo de <strong>Reinhard et al. (2001)</strong>:</p>
<ol>
<li>
<p>Convertemos tanto a imagem de saída quanto a referência para o espaço de cor Lab.</p>
</li>
<li>
<p>Calculamos a <strong>média e o desvio padrão</strong> dos canais <code>a</code> e <code>b</code> (os que representam cor).</p>
</li>
<li>
<p>Ajustamos o resultado para ter <strong>a mesma distribuição estatística</strong> de cor que a imagem de referência.</p>
</li>
<li>
<p>Isso tende a deixar a imagem com o mesmo “clima” ou tom geral (mais quente, mais frio, mais vibrante).</p>
</li>
</ol>
<p>Esse método é rápido, leve, e funciona <strong>sem alterar o modelo DDColor</strong>.</p>
<h2>Continuando<span class="hx:absolute hx:-mt-20" id="continuando"></span>
    <a href="#continuando" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Eu usei a modificação que ele sugeriu ao arquivo predict.py:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">os</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">cv2</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">torch</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">torch.nn.functional</span> <span class="k">as</span> <span class="nn">F</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">cog</span> <span class="kn">import</span> <span class="n">BasePredictor</span><span class="p">,</span> <span class="n">Input</span><span class="p">,</span> <span class="n">Path</span>
</span></span><span class="line"><span class="cl"><span class="kn">from</span> <span class="nn">basicsr.archs.ddcolor_arch</span> <span class="kn">import</span> <span class="n">DDColor</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">color_transfer</span><span class="p">(</span><span class="n">reference_bgr</span><span class="p">:</span> <span class="n">np</span><span class="o">.</span><span class="n">ndarray</span><span class="p">,</span> <span class="n">target_bgr</span><span class="p">:</span> <span class="n">np</span><span class="o">.</span><span class="n">ndarray</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">np</span><span class="o">.</span><span class="n">ndarray</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Transfer color palette of reference image to target image using Lab mean/std.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Convert BGR images to Lab color space (float32 for precision)</span>
</span></span><span class="line"><span class="cl">    <span class="n">ref_lab</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">cvtColor</span><span class="p">(</span><span class="n">reference_bgr</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">COLOR_BGR2LAB</span><span class="p">)</span><span class="o">.</span><span class="n">astype</span><span class="p">(</span><span class="s2">&#34;float32&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">tgt_lab</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">cvtColor</span><span class="p">(</span><span class="n">target_bgr</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">COLOR_BGR2LAB</span><span class="p">)</span><span class="o">.</span><span class="n">astype</span><span class="p">(</span><span class="s2">&#34;float32&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Split channels</span>
</span></span><span class="line"><span class="cl">    <span class="n">L_ref</span><span class="p">,</span> <span class="n">a_ref</span><span class="p">,</span> <span class="n">b_ref</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="n">ref_lab</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">L_tgt</span><span class="p">,</span> <span class="n">a_tgt</span><span class="p">,</span> <span class="n">b_tgt</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="n">tgt_lab</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Compute mean and std for reference and target (a and b channels)</span>
</span></span><span class="line"><span class="cl">    <span class="n">a_ref_mean</span><span class="p">,</span> <span class="n">a_ref_std</span> <span class="o">=</span> <span class="n">a_ref</span><span class="o">.</span><span class="n">mean</span><span class="p">(),</span> <span class="n">a_ref</span><span class="o">.</span><span class="n">std</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">b_ref_mean</span><span class="p">,</span> <span class="n">b_ref_std</span> <span class="o">=</span> <span class="n">b_ref</span><span class="o">.</span><span class="n">mean</span><span class="p">(),</span> <span class="n">b_ref</span><span class="o">.</span><span class="n">std</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">a_tgt_mean</span><span class="p">,</span> <span class="n">a_tgt_std</span> <span class="o">=</span> <span class="n">a_tgt</span><span class="o">.</span><span class="n">mean</span><span class="p">(),</span> <span class="n">a_tgt</span><span class="o">.</span><span class="n">std</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="n">b_tgt_mean</span><span class="p">,</span> <span class="n">b_tgt_std</span> <span class="o">=</span> <span class="n">b_tgt</span><span class="o">.</span><span class="n">mean</span><span class="p">(),</span> <span class="n">b_tgt</span><span class="o">.</span><span class="n">std</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Subtract target means, scale by reference/target std, add reference means</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># (Avoid division by zero in case of zero std)</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">a_tgt_std</span> <span class="o">&gt;</span> <span class="mf">1e-6</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">a_tgt</span> <span class="o">=</span> <span class="p">((</span><span class="n">a_tgt</span> <span class="o">-</span> <span class="n">a_tgt_mean</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">a_ref_std</span> <span class="o">/</span> <span class="n">a_tgt_std</span><span class="p">))</span> <span class="o">+</span> <span class="n">a_ref_mean</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">a_tgt</span> <span class="o">=</span> <span class="n">a_tgt</span> <span class="o">-</span> <span class="n">a_tgt_mean</span> <span class="o">+</span> <span class="n">a_ref_mean</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="n">b_tgt_std</span> <span class="o">&gt;</span> <span class="mf">1e-6</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">b_tgt</span> <span class="o">=</span> <span class="p">((</span><span class="n">b_tgt</span> <span class="o">-</span> <span class="n">b_tgt_mean</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">b_ref_std</span> <span class="o">/</span> <span class="n">b_tgt_std</span><span class="p">))</span> <span class="o">+</span> <span class="n">b_ref_mean</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="n">b_tgt</span> <span class="o">=</span> <span class="n">b_tgt</span> <span class="o">-</span> <span class="n">b_tgt_mean</span> <span class="o">+</span> <span class="n">b_ref_mean</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Clip values to valid Lab range [0,255] after transfer</span>
</span></span><span class="line"><span class="cl">    <span class="n">a_tgt</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">clip</span><span class="p">(</span><span class="n">a_tgt</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="n">b_tgt</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">clip</span><span class="p">(</span><span class="n">b_tgt</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">255</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Merge channels back (use original L from target to preserve brightness)</span>
</span></span><span class="line"><span class="cl">    <span class="n">merged_lab</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">merge</span><span class="p">([</span><span class="n">L_tgt</span><span class="p">,</span> <span class="n">a_tgt</span><span class="p">,</span> <span class="n">b_tgt</span><span class="p">])</span><span class="o">.</span><span class="n">astype</span><span class="p">(</span><span class="s2">&#34;uint8&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="c1"># Convert back to BGR color space</span>
</span></span><span class="line"><span class="cl">    <span class="n">result_bgr</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">cvtColor</span><span class="p">(</span><span class="n">merged_lab</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">COLOR_LAB2BGR</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">result_bgr</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">ImageColorizationPipeline</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;Helper pipeline to load DDColor model and process images.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">model_path</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">input_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">256</span><span class="p">,</span> <span class="n">model_size</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s2">&#34;large&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">device</span> <span class="o">=</span> <span class="n">torch</span><span class="o">.</span><span class="n">device</span><span class="p">(</span><span class="s2">&#34;cuda&#34;</span> <span class="k">if</span> <span class="n">torch</span><span class="o">.</span><span class="n">cuda</span><span class="o">.</span><span class="n">is_available</span><span class="p">()</span> <span class="k">else</span> <span class="s2">&#34;cpu&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Choose encoder backbone based on model size</span>
</span></span><span class="line"><span class="cl">        <span class="n">encoder_name</span> <span class="o">=</span> <span class="s2">&#34;convnext-t&#34;</span> <span class="k">if</span> <span class="n">model_size</span> <span class="o">==</span> <span class="s2">&#34;tiny&#34;</span> <span class="k">else</span> <span class="s2">&#34;convnext-l&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Initialize DDColor model</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">model</span> <span class="o">=</span> <span class="n">DDColor</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">encoder_name</span><span class="o">=</span><span class="n">encoder_name</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">decoder_name</span><span class="o">=</span><span class="s2">&#34;MultiScaleColorDecoder&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">input_size</span><span class="o">=</span><span class="p">[</span><span class="n">input_size</span><span class="p">,</span> <span class="n">input_size</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">            <span class="n">num_output_channels</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span>    <span class="c1"># model predicts ab channels</span>
</span></span><span class="line"><span class="cl">            <span class="n">last_norm</span><span class="o">=</span><span class="s2">&#34;Spectral&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">do_normalize</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">num_queries</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">num_scales</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">dec_layers</span><span class="o">=</span><span class="mi">9</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">)</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">device</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Load weights</span>
</span></span><span class="line"><span class="cl">        <span class="n">state</span> <span class="o">=</span> <span class="n">torch</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="n">model_path</span><span class="p">,</span> <span class="n">map_location</span><span class="o">=</span><span class="s2">&#34;cpu&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Some checkpoints store weights under &#39;params&#39; key</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">load_state_dict</span><span class="p">(</span><span class="n">state</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&#34;params&#34;</span><span class="p">,</span> <span class="n">state</span><span class="p">),</span> <span class="n">strict</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="o">.</span><span class="n">eval</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">input_size</span> <span class="o">=</span> <span class="n">input_size</span>  <span class="c1"># store for processing</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nd">@torch.no_grad</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">process</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">img_bgr</span><span class="p">:</span> <span class="n">np</span><span class="o">.</span><span class="n">ndarray</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">np</span><span class="o">.</span><span class="n">ndarray</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;&#34;Colorize a BGR image (numpy array) using the loaded DDColor model.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Preserve original resolution L channel</span>
</span></span><span class="line"><span class="cl">        <span class="n">orig_h</span><span class="p">,</span> <span class="n">orig_w</span> <span class="o">=</span> <span class="n">img_bgr</span><span class="o">.</span><span class="n">shape</span><span class="p">[:</span><span class="mi">2</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Convert to Lab and extract L channel at original size</span>
</span></span><span class="line"><span class="cl">        <span class="n">img_lab</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">cvtColor</span><span class="p">(</span><span class="n">img_bgr</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">COLOR_BGR2LAB</span><span class="p">)</span><span class="o">.</span><span class="n">astype</span><span class="p">(</span><span class="s2">&#34;float32&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">orig_L</span> <span class="o">=</span> <span class="n">img_lab</span><span class="p">[:,</span> <span class="p">:,</span> <span class="mi">0</span><span class="p">:</span><span class="mi">1</span><span class="p">]</span>  <span class="c1"># shape (H, W, 1)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Prepare grayscale input at model resolution</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Resize input to model expected size</span>
</span></span><span class="line"><span class="cl">        <span class="n">inp</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">resize</span><span class="p">(</span><span class="n">img_bgr</span><span class="p">,</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">input_size</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">input_size</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="n">inp_lab</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">cvtColor</span><span class="p">(</span><span class="n">inp</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">COLOR_BGR2LAB</span><span class="p">)</span><span class="o">.</span><span class="n">astype</span><span class="p">(</span><span class="s2">&#34;float32&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">L</span> <span class="o">=</span> <span class="n">inp_lab</span><span class="p">[:,</span> <span class="p">:,</span> <span class="mi">0</span><span class="p">:</span><span class="mi">1</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Create grayscale Lab image by zeroing AB channels</span>
</span></span><span class="line"><span class="cl">        <span class="n">gray_lab</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">L</span><span class="p">,</span> <span class="n">np</span><span class="o">.</span><span class="n">zeros_like</span><span class="p">(</span><span class="n">L</span><span class="p">),</span> <span class="n">np</span><span class="o">.</span><span class="n">zeros_like</span><span class="p">(</span><span class="n">L</span><span class="p">)],</span> <span class="n">axis</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Convert back to RGB (now it&#39;s a gray RGB image of size input_size)</span>
</span></span><span class="line"><span class="cl">        <span class="n">gray_rgb</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">cvtColor</span><span class="p">(</span><span class="n">gray_lab</span><span class="o">.</span><span class="n">astype</span><span class="p">(</span><span class="s2">&#34;uint8&#34;</span><span class="p">),</span> <span class="n">cv2</span><span class="o">.</span><span class="n">COLOR_LAB2RGB</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Prepare tensor and run model</span>
</span></span><span class="line"><span class="cl">        <span class="n">tensor</span> <span class="o">=</span> <span class="n">torch</span><span class="o">.</span><span class="n">from_numpy</span><span class="p">(</span><span class="n">gray_rgb</span><span class="o">.</span><span class="n">transpose</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span><span class="o">.</span><span class="n">float</span><span class="p">()</span><span class="o">.</span><span class="n">unsqueeze</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="o">.</span><span class="n">to</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">device</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">out_ab</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">model</span><span class="p">(</span><span class="n">tensor</span><span class="p">)</span>  <span class="c1"># output is (1,2,H_out,W_out) in Lab AB</span>
</span></span><span class="line"><span class="cl">        <span class="n">out_ab</span> <span class="o">=</span> <span class="n">out_ab</span><span class="o">.</span><span class="n">cpu</span><span class="p">()</span><span class="o">.</span><span class="n">float</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Resize output AB to original image size</span>
</span></span><span class="line"><span class="cl">        <span class="n">out_ab_resized</span> <span class="o">=</span> <span class="n">F</span><span class="o">.</span><span class="n">interpolate</span><span class="p">(</span><span class="n">out_ab</span><span class="p">,</span> <span class="n">size</span><span class="o">=</span><span class="p">(</span><span class="n">orig_h</span><span class="p">,</span> <span class="n">orig_w</span><span class="p">),</span> <span class="n">mode</span><span class="o">=</span><span class="s2">&#34;bilinear&#34;</span><span class="p">,</span> <span class="n">align_corners</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">out_ab_resized</span> <span class="o">=</span> <span class="n">out_ab_resized</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">numpy</span><span class="p">()</span><span class="o">.</span><span class="n">transpose</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>  <span class="c1"># (H, W, 2)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Combine original L and predicted AB, then convert to BGR</span>
</span></span><span class="line"><span class="cl">        <span class="n">out_lab</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">concatenate</span><span class="p">([</span><span class="n">orig_L</span><span class="p">,</span> <span class="n">out_ab_resized</span><span class="p">],</span> <span class="n">axis</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">astype</span><span class="p">(</span><span class="s2">&#34;uint8&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="n">out_bgr</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">cvtColor</span><span class="p">(</span><span class="n">out_lab</span><span class="p">,</span> <span class="n">cv2</span><span class="o">.</span><span class="n">COLOR_LAB2BGR</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">out_bgr</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">Predictor</span><span class="p">(</span><span class="n">BasePredictor</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">setup</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;&#34;Load models into memory for efficient multiple predictions.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Determine paths for large and tiny model weights</span>
</span></span><span class="line"><span class="cl">        <span class="n">large_model_path</span> <span class="o">=</span> <span class="s2">&#34;checkpoints/ddcolor_modelscope.pth&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">tiny_model_path</span>  <span class="o">=</span> <span class="s2">&#34;checkpoints/ddcolor_paper_tiny.pth&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># (Ensure the above files exist. If not, download from ModelScope or HuggingFace as described.)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Initialize pipelines for large and tiny models</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">colorizer_large</span> <span class="o">=</span> <span class="n">ImageColorizationPipeline</span><span class="p">(</span><span class="n">model_path</span><span class="o">=</span><span class="n">large_model_path</span><span class="p">,</span> <span class="n">input_size</span><span class="o">=</span><span class="mi">512</span><span class="p">,</span> <span class="n">model_size</span><span class="o">=</span><span class="s2">&#34;large&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">colorizer_tiny</span>  <span class="o">=</span> <span class="n">ImageColorizationPipeline</span><span class="p">(</span><span class="n">model_path</span><span class="o">=</span><span class="n">tiny_model_path</span><span class="p">,</span>  <span class="n">input_size</span><span class="o">=</span><span class="mi">512</span><span class="p">,</span> <span class="n">model_size</span><span class="o">=</span><span class="s2">&#34;tiny&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">predict</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="n">image</span><span class="p">:</span> <span class="n">Path</span> <span class="o">=</span> <span class="n">Input</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;Grayscale input image.&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">reference</span><span class="p">:</span> <span class="n">Path</span> <span class="o">=</span> <span class="n">Input</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;Optional reference image to guide colors&#34;</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">        <span class="n">model_size</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">Input</span><span class="p">(</span><span class="n">description</span><span class="o">=</span><span class="s2">&#34;Model size to use (large = highest quality, tiny = faster).&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                                <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s2">&#34;large&#34;</span><span class="p">,</span> <span class="s2">&#34;tiny&#34;</span><span class="p">],</span> <span class="n">default</span><span class="o">=</span><span class="s2">&#34;large&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Path</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;&#34;&#34;Run a single prediction. Colorizes the image, optionally using a reference image for color bias.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Read input image</span>
</span></span><span class="line"><span class="cl">        <span class="n">img</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">imread</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">image</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">img</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&#34;Failed to load input image&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Select model pipeline</span>
</span></span><span class="line"><span class="cl">        <span class="n">pipeline</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">colorizer_tiny</span> <span class="k">if</span> <span class="n">model_size</span> <span class="o">==</span> <span class="s2">&#34;tiny&#34;</span> <span class="k">else</span> <span class="bp">self</span><span class="o">.</span><span class="n">colorizer_large</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Colorize the image using DDColor</span>
</span></span><span class="line"><span class="cl">        <span class="n">output_bgr</span> <span class="o">=</span> <span class="n">pipeline</span><span class="o">.</span><span class="n">process</span><span class="p">(</span><span class="n">img</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># If a reference image is provided, transfer its color style to the output</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="n">reference</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="n">ref_img</span> <span class="o">=</span> <span class="n">cv2</span><span class="o">.</span><span class="n">imread</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">reference</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">ref_img</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&#34;Failed to load reference image&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="c1"># Only apply transfer if reference is a color image</span>
</span></span><span class="line"><span class="cl">            <span class="k">if</span> <span class="n">ref_img</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">                <span class="n">output_bgr</span> <span class="o">=</span> <span class="n">color_transfer</span><span class="p">(</span><span class="n">ref_img</span><span class="p">,</span> <span class="n">output_bgr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># Save result</span>
</span></span><span class="line"><span class="cl">        <span class="n">out_path</span> <span class="o">=</span> <span class="s2">&#34;/tmp/out.png&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">cv2</span><span class="o">.</span><span class="n">imwrite</span><span class="p">(</span><span class="n">out_path</span><span class="p">,</span> <span class="n">output_bgr</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">Path</span><span class="p">(</span><span class="n">out_path</span><span class="p">)</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Com essa modificação posso rodar com esse novo parâmetro, note a opção &ldquo;&ndash;ref&rdquo; que tem agora e coloque sua imagem no lugar certo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>❯ docker run --rm --gpus all \
  -v &#34;$PWD/input&#34;:/workspace/input_images:ro \
  -v &#34;$PWD/results&#34;:/workspace/results:rw \
  -v &#34;$PWD/ref_images&#34;:/workspace/ref_images:ro \
  -v &#34;$PWD/modelscope&#34;:/app/modelscope:ro \
  -v &#34;$PWD/infer.py&#34;:/app/infer.py:ro \
  ddcolor:latest \
  python3 /app/infer.py \
    --model_path /app/modelscope/damo/cv_ddcolor_image-colorization/pytorch_model.pt \
    --input /workspace/input_images \
    --ref /workspace/ref_images/images.jpg \
    --output /workspace/results</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Passei outra foto colorida de referência mas não dferença no resultado. Novamente, perguntei ao ChatGPT:</p>
<h3>Por que você não percebeu diferença no resultado?<span class="hx:absolute hx:-mt-20" id="por-que-você-não-percebeu-diferença-no-resultado"></span>
    <a href="#por-que-voc%c3%aa-n%c3%a3o-percebeu-diferen%c3%a7a-no-resultado" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A <strong>transferência global de cor</strong> só altera a “paleta geral” — e não as cores de cada objeto.</p>
<p>Se a imagem de referência tem tons suaves ou parecidos com os que o DDColor já aplicaria automaticamente, o efeito final <strong>vai parecer idêntico</strong>.</p>
<p>Mas se você usar como referência uma imagem bem saturada (por exemplo, um céu alaranjado, ou um ambiente azul escuro), você verá a saída do DDColor ser <strong>puxada visualmente</strong> para aquele estilo.</p>
<h2>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Segui a sugestão e baixei outra imagem nada a ver, colorida, super saturada com cores vibrantes pra ver se fazia diferença. Esta é a imagem de referência que passei pro Docker:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/ryptql9u8mk04o3r7ah01fk1qz5q" alt="Imagem de Referência"  loading="lazy" /></p>
<p>E eis o novo resultado aplicando ela à imagem anterior em pós-processamento (esse passo não é mais I.A. é pós-processamento de imagem mesmo).</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/cxcwwnj2ncszvmv4whj4qesafgnm" alt="Foto pós-processada"  loading="lazy" /></p>
<p>Comparem com a outra foto re-colorizada, vou colocar aqui embaixo de novo pra dar pra comparar:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/o2r085pzzpsavbyr39btg1vq0hwp" alt="Repetindo a primeira re-colorização"  loading="lazy" /></p>
<p>Entenderam? Agora precisa ficar testando com referências diferentes pra ver a influência no resultado final, mas de fato, ele consegue aplicar o algoritmo de Reinhard e fazer uma transferência global de cor. Como o nome diz, é &ldquo;global&rdquo;, não &ldquo;por objeto&rdquo;, então é difícil conseguir controlar só partes da imagem e sim a imagem toda. Mas em teoria eu acho que você conseguiria fazer uma colorização manual no Photoshop com cores saturadas, perto de onde quer influenciar, pra tentar ajustar, mas eu não tentei isso ainda, depois se alguém fizer, mande nos comentários.</p>
<p>Mas é isso. Agora pelo menos eu consigo me divertir pegando fotos antigas de família e re-colorizar.</p>
]]></content:encoded><category>midia-generativa</category><category>containers</category><category>tutoriais</category></item><item><title>[Akitando] #128 - Entendendo Pacotes com Slackware | Deb, Apt, Tarbals</title><link>https://akitaonrails.github.io/2022/09/25/akitando-128-entendendo-pacotes-com-slackware-deb-apt-tarbals/</link><guid isPermaLink="true">https://akitaonrails.github.io/2022/09/25/akitando-128-entendendo-pacotes-com-slackware-deb-apt-tarbals/</guid><pubDate>Sun, 25 Sep 2022 12:49:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/iQkBbRPkASo"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Como gerenciadores de pacotes como APT funcionam? O que tem num pacote? Vamos instalar um Slackware, uma das distros de Linux mais antigas, e ver como evoluímos de gerenciar tarballs pra gerenciar resolução de dependências com pacotes modernos como formato DEB.&lt;/p&gt;
&lt;p&gt;Hoje você vai começar a entender como software num Linux é organizado de verdade.&lt;/p&gt;
&lt;p&gt;== Conteúdo&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;00:00 - Intro&lt;/li&gt;
&lt;li&gt;01:21 - CAP 1 - Como nós, reviewers, trapaceamos&lt;/li&gt;
&lt;li&gt;04:15 - CAP 2 - Instalação como nos Anos 90: Slackware&lt;/li&gt;
&lt;li&gt;06:24 - CAP 3 - Particionando o Drive: CFDISK&lt;/li&gt;
&lt;li&gt;10:32 - CAP 4 - Filesystems e Mount Points&lt;/li&gt;
&lt;li&gt;16:36 - CAP 5 - Como APT instala pacotes? Formato DEB&lt;/li&gt;
&lt;li&gt;25:14 - CAP 6 - Formato de Pacotes Original: pacotes no Slackware&lt;/li&gt;
&lt;li&gt;31:57 - CAP 7 - Mantenedores de Distros = Mantenedores de Pacotes Binários: SlackBuilds.org&lt;/li&gt;
&lt;li&gt;40:31 - CAP 8 - Slackware nos dias de hoje: Nostalgia&lt;/li&gt;
&lt;li&gt;43:02 - Bloopers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;== Links&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/iQkBbRPkASo"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Como gerenciadores de pacotes como APT funcionam? O que tem num pacote? Vamos instalar um Slackware, uma das distros de Linux mais antigas, e ver como evoluímos de gerenciar tarballs pra gerenciar resolução de dependências com pacotes modernos como formato DEB.</p>
<p>Hoje você vai começar a entender como software num Linux é organizado de verdade.</p>
<p>== Conteúdo</p>
<ul>
<li>00:00 - Intro</li>
<li>01:21 - CAP 1 - Como nós, reviewers, trapaceamos</li>
<li>04:15 - CAP 2 - Instalação como nos Anos 90: Slackware</li>
<li>06:24 - CAP 3 - Particionando o Drive: CFDISK</li>
<li>10:32 - CAP 4 - Filesystems e Mount Points</li>
<li>16:36 - CAP 5 - Como APT instala pacotes? Formato DEB</li>
<li>25:14 - CAP 6 - Formato de Pacotes Original: pacotes no Slackware</li>
<li>31:57 - CAP 7 - Mantenedores de Distros = Mantenedores de Pacotes Binários: SlackBuilds.org</li>
<li>40:31 - CAP 8 - Slackware nos dias de hoje: Nostalgia</li>
<li>43:02 - Bloopers</li>
</ul>
<p>== Links</p>
<ul>
<li>Build &amp; Install Slackware Packages Automatically (<a href="https://medium.com/netdef/build-install-slackware-packages-automatically-b2986d2f86f9"target="_blank" rel="noopener">https://medium.com/netdef/build-install-slackware-packages-automatically-b2986d2f86f9</a>)</li>
<li>SlackBuilds.org (<a href="https://slackbuilds.org/"target="_blank" rel="noopener">https://slackbuilds.org/</a>)</li>
<li>SBOPKG (<a href="https://sbopkg.org/"target="_blank" rel="noopener">https://sbopkg.org/</a>)</li>
</ul>
<h2>SCRIPT<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Vamos continuar a minissérie de Linux falando mais sobre o que tem por baixo do capô. No video anterior vimos um pouco sobre como é a sequência de boot, desde ligar o computador até chegar na tela de login. E hoje quero apresentar mais conceitos de Linux destacando alguns trechos da instalação da distribuição Linux mais antiga ainda em funcionamento hoje: o lendário Slackware. Vamos lá.</p>
<p>(&hellip;)</p>
<p>Eu vinha dizendo em redes sociais e até alguns videos anteriores que eu recomendo Arch Linux pra você começar a aprender um pouco mais sobre como Linux funciona. Tanto porque a instalação de um Arch é menos fácil que de um Ubuntu ou Fedora, mas também porque o Wiki do Arch é uma das documentações online mais completa sobre todos os aspectos de um Linux.</p>
<p>Distros modernas como Ubuntu, PopOS, Mint e coisas assim são hiper triviais de instalar, graças a LiveCDs e instalador gráfico como Calamares, que já fazem o grosso do trabalho pra você. Mas mesmo o Arch não é tão complicado assim e na verdade ele já esconde muito da complexidade. Portanto, instalar Linux moderno hoje em dia não ensina quase nada, a menos que seu hardware seja muito exótico e tenha pouco suporte, daí você vai ser obrigado a descer mais fundo nas suas pesquisas até conseguir fazer tudo funcionar.</p>
<p>Canais de reviews também trapaceiam um pouco, e eu vou fazer a mesma coisa. A coisa mais chata na instalação de qualquer sistema operacional, seja Windows ou qualquer distro Linux, é suporte a hardware porcaria que não tem drivers bons disponíveis. Dispositivos de marcas chinesas duvidosas compradas numa Shopee ou AliExpress, webcam baratinha que, quando você conecta, seu sistema nem detecta automaticamente e coisas assim. O maior problema em qualquer sistema operacional é ter bons drivers que desestabilizem tudo.</p>
<p>Mas a trapaça que eu falo é que a gente que grava video, não vai ficar testando instalação em vários tipos diferentes de combinações de hardware. Normalmente temos uma máquina separada só pra isso que não é nosso PC primário. Claro, sem nenhum componente muito exótico, que a maioria dos instaladores vai auto detectar de primeira sem problema. Ou vamos usar máquinas virtuais. E a vantagem de usar um VirtualBox, VMWare, QEMU/KVM é que o hardware virtual dessas máquinas é bem documentado e tem bons drivers disponíveis. Qualquer instalador de Linux detecta que tá numa máquina virtual e instala os corretos sem nenhum problema. Por isso que você assiste video de canal de YouTube e parece que foi tão fácil.</p>
<p>Entenda que hoje em dia, mesmo Windows, até que funciona direitinho. Quando você tem problemas, normalmente são drivers ruins, ou atualização automática que baixou uma versão nova de driver com bugs. Campeão de bugs são drivers de placas de video como NVIDIA. Saem atualizações com bastante frequência, são pacotes grandes, e vira e mexe chega um bug novo que te fode. Gamers sabem bem o que é isso. E por isso também notebooks de boas marcas costumam ter poucos problemas, porque os fabricantes tendem a arredondar bem os drivers dos componentes da máquina, e fazem pouca ou nenhuma atualização depois que tudo já funciona.</p>
<p>Aí você só vai ter problema se o sistema operacional ganhar um upgrade que quebra compatibilidade com seus drivers. Mas no fim, o problema sempre são drivers. Por isso sempre pesquise se todos os componentes que você compra costuma ter drivers disponíveis que são atualizados com uma frequência razoável pra corrigir bugs. Se você tá pesquisando, digamos, uma placa de captura de video, vai no site deles. Se descobrir que a última vez que eles lançaram um driver novo foi uns 3 anos atrás, desiste dessa compra.</p>
<p>Isso tudo dito, eu já tinha mostrado no video anterior uma instalação rápida de Ubuntu numa máquina virtual, e em poucos minutos tava tudo detectado e instalado. Zero problemas. Por isso quero instalar um Slackware hoje. Slackware foi a primeira distro que eu tentei instalar, lá por volta de 1995, acho que ainda era 1.0, que vinha num CD junto com um livro importado ensinando sobre Linux. Eu acho que na época não consegui instalar e foi super frustrante, eu só voltei a tentar Linux de novo quando um amigo da faculdade me emprestou um Red Hat versão 4, com um instalador um pouco menos difícil e consegui ir até subir o X pela primeira vez.</p>
<p>Se você só instalou Linux modernos, hoje vai ter um choque, porque o Slackware moderno ainda é muito parecido com o que eu lembro de mais de 20 anos atrás. E como disclaimer, o video de hoje não é um tutorial. Não dá pra seguir passo a passo. Quero aproveitar a instalação pra ir destacando alguns conceitos que acho importante saber. Então vamos bootar o Slackware numa máquina virtual, e logo de cara você já vai tomar um susto. Não abre interface gráfica, cai direto numa linha de comando. E sim, LEIA a porra dos textos que aparecem. 90% dos erros de iniciantes é ser preguiçoso e não ler com atenção o que tá escrito. Se alguma coisa escreve na sua tela é porque você deveria ler.</p>
<p>Pessoal pensa que LiveCDs que são esses Linux que bootam de pendrive servem só pra instalar a distro no seu HD. Mas na realidade, todo LiveCD tem uma segunda utilidade: ser usado pra conseguir recuperar o Linux do seu PC caso faça alguma bobagem e não consiga mais bootar dele. Essa máquina virtual, por exemplo, tá vazia, mas já estamos dentro de um ambiente Linux. Como ele explica aqui no texto, daqui você pode montar o HD da sua máquina que já tinha uma instalação e no mínimo ter acesso aos seus arquivos, caso não esteja conseguindo bootar normalmente. Aqui ele ensina como fazer isso.</p>
<p>Damos enter e olha o que expliquei no episódio anterior: executa o binariozão da kernel do Linux, cria uma partição em RAM que é o initramfs e descomprime a imagem initrd nela pra fazer o segundo estágio do boot. Aí a kernel executa um gerenciador de serviços, um init system, que é processo número 1 que vai iniciar os serviços do runlevel certo. Lembram? Expliquei tudo isso no video anterior. E quando terminar o boot, vai perguntar se quer trocar o layout do seu teclado, caso esteja usando um layout brasileiro, por exemplo.</p>
<p>Como é um LiveCD, tem só o usuário root sem senha e de novo, leia a porra do texto, porque já te dá a dica que o próximo passo pra continuar é que vai precisar criar uma partição e formatar antes de conseguir instalar qualquer coisa. E uma coisa que imagino que ninguém vai ter é problema de pouca memória, mas digamos que seu PC seja tão velho que tenha menos de 4 gigas de RAM, daí ele recomenda também criar uma partição de swap. E vamos fazer as duas coisas agora.</p>
<p>Pra criar partições, nos anos 90 eu seria obrigado a usar a antiga ferramenta <code>fdisk</code>. Era horrível, porque precisava calcular manualmente a quantidade de megas baseado na multiplicação de coisas como tamanho de setor, cilindros, cabeças, coisas que expliquei no video sobre partições. Mas hoje em dia temos uma ferramenta melhor, o bom e velho <code>cfdisk</code>. A opção mais padrão agora é escolher a etiqueta de GPT, que também explico no video que falei.</p>
<p>Normalmente um Linux dá o nome de &ldquo;/dev/sda&rdquo; pro primeiro HD que detecta. Se tiver um segundo HD o nome seria &ldquo;/dev/sdb&rdquo;. No caso temos 80 giga de espaço livre e vamos criar a primeira partição que vai se chamar &ldquo;/dev/sda1&rdquo;. Com setinhas, mudo os itens de menu abaixo, escolho &ldquo;new&rdquo; e digito que tamanho quero que essa partição tenha. Posso digitar &ldquo;100M&rdquo;, onde &ldquo;M&rdquo; quer dizer &ldquo;megabytes&rdquo;. Essa vai ser a partição de boot. 100 mega acho que chutei pouco, poderia ser mais. Pra estar seguro, digita uns 150 megas num PC de verdade.</p>
<p>Daí no menu embaixo, movo pra direita pra escolher a opção de &ldquo;tipo&rdquo; e mudo pra &ldquo;EFI System&rdquo;. Tá vendo? Se você assistiu meus videos sobre armazenamento, tudo isso de fdisk, partição, tipos, EFI, já expliquei tudo antes. Agora eu movo a setinha pra baixo pra escolher o resto do espaço livre, escolho a opção de nova partição, e chuto uns 4G, onde &ldquo;G&rdquo; significa &ldquo;gigabytes&rdquo;. Essa vai ser a partição &ldquo;sda2&rdquo;, daí mudo o tipo pra ser Linux Swap. Não existe tamanho certo pra essa partição. Se tem bastante RAM, um swap pequeno só pra emergência tá bom. Se tem pouca RAM, recomendo comprar mais RAM. Pouca RAM é abaixo de 8 giga. Um swap de 4 giga ou menos tá de bom tamanho.</p>
<p>Finalmente, selecionamos o espaço que sobrou e criamos a maior partição, que aqui vai ser a &ldquo;sda3&rdquo; com mais de 70G e escolhemos o tipo de &ldquo;Linux filesystem&rdquo;. Esses tipos são só etiquetas. Elas não mudam nada, é que nem uma hashtag de twitter. Só pra depois ficar mais fácil saber o que é cada partição. E notem como a ordem dessas partições não faz diferença. Só a partição de boot que é bom ser a primeira, mas eu poderia ter invertido e deixado a principal como &ldquo;sda2&rdquo; e o swap como &ldquo;sda3&rdquo;. Poderia ter criado uma quarta partição pra ser onde ficaria meu diretório home, pra facilitar coisas como backup e recuperação depois. Tem várias estratégias, mas o que fiz agora é o hello world, o mais simples. A maioria dos instaladores gráficos, por baixo dos panos, faz a mesma coisa sem você saber.</p>
<p>Partição é só uma forma de deixar gravado nos primeiros bytes do HD que do byte X até o byte Y está reservado pra partição 1, do byte Y até o byte Z é a partição 2, e assim por diante. É só isso. Mas pra usar essas partições, o sistema operacional precisa saber que formato é pra usar dentro dela, e pra isso que formatamos. E em linux, cada sistema de arquivos tem uma ferramenta &ldquo;mkfs&rdquo; que é literalmente &ldquo;make filesystem&rdquo;, por exemplo, vamos formatar a primeira partição de boot &ldquo;sda1&rdquo; como FAT32 com o comando <code>mkfs.fat -F 32 /dev/sda1</code>. Pronto, apesar desses avisos, não deu erro e agora tá formatado.</p>
<p>A segunda partição &ldquo;sda2&rdquo; é o swap. Lembram? Swap é usado quando tamos ficando com pouca memória RAM, daí o Linux passa a mapear parte do disco como memória adicional pra não crashear tudo. Só que HD é sempre MUITO mais lento que RAM, por isso um bom sistema operacional como Linux só vai usar swap se realmente precisar muito. Swap só vale a pena se estiver usando um SSD NVME, que é rápido o suficiente pra não deixar sua máquina muito lenta. De qualquer forma, precisamos formatar essa partição com um sistema de arquivos especial que vai simular memória, e pra isso usamos o comando <code>mkswap /dev/sda2</code> que é literalmente &ldquo;make swap&rdquo;.</p>
<p>Mas não basta formatar, temos que dizer pro sistema que é pra usar como swap e pra isso tem o comando <code>swapon /dev/sda2</code>, que é literalmente &ldquo;swap on&rdquo;, de ligar. Se quisermos desligar pro sistema não usar mais essa partição tem o comando oposto que é <code>swapfoff</code>. E pronto, temos swap configurado. Finalmente, vamos formatar a partição principal que é onde o Slackware vai se instalar e onde vai ficar os arquivos do sistema. A opção padrão mais fácil é formatar em ext4.</p>
<p>Se for um notebook, recomendo antes encriptar a partição com LUKS e dentro criar um volume formatado com ext4 ou usar um sistema de arquivos mais moderno como btrfs ou ZFS que começou a vir nos Ubuntu a partir da versão 19 ou 20. Vou falar um pouco mais sobre isso em outro video. Mas na dúvida, escolha ext4 que é o filesystem mais usado no mundo Linux e todo ferramental que vem em todas as distros já assume que está usando ext4. De novo, se nunca ouviu falar de ext4, btrfs, zfs, fat, é porque não assistiu meu video sobre sistemas de arquivos. Anota aí pra assistir depois.</p>
<p>Pronto, agora o HD foi particionado, e cada partição foi formatada com sistemas de arquivos que um Linux entende. Agora precisamos dar acesso a essas partições. E a forma de fazer isso é criando um mount point. Vou falar mais de mount points no próximo video, mas por hoje entenda o seguinte. Um &ldquo;/dev/sda3&rdquo; é só o caminho pra um linguição de bits. O sistema não entende arquivos e diretórios, só entende ler blocos de bits e gravar blocos de bits. Lembra o que eu sempre falo? Um dispositivo de armazenamento é um block device, um dispositivo de blocos. O sistema operacional precisa carregar um intérprete pra traduzir conjuntos de blocos em arquivos. Esse é o papel de um sistema de arquivos como ext4.</p>
<p>E pra carregar esse intérprete, fazemos um ponto de montagem. Um mount point é só um diretório onde digo: a partir deste diretório, interprete os blocos do dispositivo como uma árvore de diretórios. Use o formato ext4 pra ler e gravar diretórios e arquivos. Então podemos usar o comando <code>mount /dev/sda3 /mnt</code>. Tá implícito aqui, mas o comando mais completo seria <code>mount -t ext4 /dev/sda3 /mnt</code>. De qualquer forma, a partir de &ldquo;/mnt&rdquo; é a raíz da partição sda3.</p>
<p>A partir daqui, tudo que gravar em &ldquo;/mnt&rdquo; na realidade vai gravar dentro da partição sda3. E dentro crio o diretório &ldquo;/boot/efi&rdquo;, onde vou montar a primeira partição de boot, o sda1 com <code>mount /dev/sda1 /mnt/boot/efi</code>. Isso realmente não teria como você saber de cabeça se nunca configurou EFI. E, de novo, se não sabe a diferença de BIOS e EFI, de MBR e GPT, é porque não assistiu meu outro video sobre isso. Mas saiba que toda instalação automática de Linux tá criando esses mount points por baixo dos panos pra você. Só que o Slackware me obriga a fazer isso manualmente.</p>
<p>Sendo honesto, eu não precisava ter formatado as partições na linha de comando com os comandos de &ldquo;mkfs&rdquo;. O Slackware tem um instalador rudimentar que sabe fazer essas coisas. Mas eu quis mostrar como é manualmente. Apesar que partição de boot EFI você ia ter que fazer na mão de qualquer jeito porque o instalador do Slackware não configura isso, então não fizemos nada muito extra. Mas agora podemos digitar <code>setup</code> e vai abrir esse menu de opções. Na opção TARGET, olha só, detectou minhas partições e sabe que &ldquo;sda2&rdquo; é o swap porque no cfdisk demos o tipo de &ldquo;linux swap&rdquo;, lembra? Daí ele se oferece pra fazer o mkswap e swapon, mas como já fizemos isso antes, só dar &ldquo;não&rdquo; pra continuar.</p>
<p>Agora ele achou a partição principal &ldquo;sda3&rdquo; e diz que vai usar pra ser o mount point &ldquo;/&rdquo; que é a raíz do sistema de arquivos, o diretório &ldquo;/&rdquo; principal. Se tivesse feito outras partições, aqui poderia escolher pra montar como &ldquo;/home&rdquo;, por exemplo. O que essa ferramenta tá fazendo é preparando o arquivo &ldquo;/etc/fstab&rdquo;, que é como o sistema init no boot sabe como montar as partições do seu HD automaticamente. De novo, ele se oferece pra formatar essa partição, mas como já fizemos isso, damos &ldquo;no&rdquo; de novo.</p>
<p>Depois de ter as partições devidamente configuradas é hora de copiar os arquivos do sistema pra lá. A primeira opção é justamente copiar tudo a partir do seu pendrive que usou pra bootar, ou no caso da minha máquina virtual, direto a partir do arquivo de ISO que baixamos. Mas de curiosidade veja essas outras opções aqui embaixo. Ele tem opção de instalar a partir de um diretório compartilhado na rede ou via servidor de arquivos FTP. Quem é dos anos 90 vai se lembrar disso, porque nos anos 90 não ia ter como dar boot via pendrive como fazemos hoje. Pendrive nem existia ainda.</p>
<p>Normalmente ia ter 2 disquetes ou floppy disks de 1.44 megabytes cada. Um era o disco de boot, que carrega a kernel e o segundo era o disco de root, que acho que é onde tinha o initrd e as ferramentas de instalação. Daí precisava ter o resto dos pacotes num CD ou DVD. Veja o tamanho deste arquivo de ISO, mais de 3 gigabytes. Um CD na época tinha menos de 700 megabytes, então esse Slackware ia precisar de mais de 4 CDs pra instalar completo.</p>
<p>Lá pelo meio dos anos 90, DVD também não era acessível. Então íamos precisar de 2 disquetes, mais uns 4 CDs. Mas se estivéssemos numa boa faculdade ou empresa moderna, talvez tivesse os pacotes num servidor FTP. Daí dava pra instalar pela rede em vez de ter que ficar trocando CD. A gente que curtia Linux tinha tubos com dezenas de CDs. E CD é lento, levava horas pra instalar. Por isso eu falo que hoje tá fácil. Em menos de 1 hora dá pra sair do zero pra um Linux todo configurado e funcionando. Bons tempos.</p>
<p>Enfim, agora é só ir escolhendo as opções padrão, que é basicamente não ficar tirando nada e deixar o Slackware instalar tudo completo. O objetivo todo de instalar Slackware foi justamente pra falar um pouco sobre os primórdios do que chamamos de pacotes. Olha só o instalador, ele tá nervoso instalando um pacote atrás do outro, mas o que diabos é isso de pacote? Vamos fazer uma tangente.</p>
<p>Se você é programador e já usou um Ubuntu da vida, deve estar acostumado a copiar e colar comandos como <code>apt install docker</code>, acender uma vela, e torcer pra não dar nenhum erro, porque você não tem idéia do que fazer se der erro. Se estiver num Fedora, já deve ter digitado <code>dnf install docker</code>, se estiver num openSuse, digita <code>zypper install docker</code>, ou se estiver num Arch Linux vai ser <code>yay -S docker</code>. Apt, Dnf, Zypper, Yay, Pacman, Portage, Apk, são o que chamamos de &ldquo;gerenciadores de pacotes&rdquo;, justamente porque eles sabem como baixar e instalar ou desinstalar os softwares que vem dentro desses pacotes. Cada distro diferente costuma ter gerenciadores diferentes.</p>
<p>Slackware vem de uma época que precede o conceito de gerenciar pacotes. Vamos entender pacotes primeiro. Enquanto o Slackware fica ali instalando pacotes, vamos abrir outra máquina virtual com o Ubuntu 22 que mostrei no episódio anterior. Eu tenho instalado aqui um programa que todo programador já deve ter visto, o htop. Olha só ele mostra informações do sistema, quanto de carga cada core da minha CPU tá puxando, quanto de memória tá sobrando, e embaixo uma lista com todos os processos rodando na máquina, quanto de recursos cada um tá usando. É uma excelente ferramenta de monitoramento pra você saber o que tá acontecendo na sua máquina.</p>
<p>Onde fica o binário executável do htop? Do terminal podemos usar o comando <code>which htop</code>, que vai vasculhar os PATHs que tão configurados no seu shell e ele acha o binário em <code>/usr/bin/htop</code>. Se não tivesse essa variável PATH configurada, precisaria digitar o caminho completo <code>/usr/bin/htop</code> pra executar. O PATH é uma conveniência pra facilitar nossa vida na hora de digitar comandos. Enfim, o htop não tá todo contido nesse binário. Ele tem dependência com outros objetos binários, bibliotecas instaladas no sistema. A gente pode saber quais são usando o comando <code>ldd /usr/bin/htop</code> e olha só, ele depende desse objeto binário libtinfo.so.6, a mais comum em todos os binários que é a libc ou libc.so.6 e assim por diante.</p>
<p>Vamos estragar o htop. Deixa eu mover o libncursesw.so.6 pro diretório /tmp e tentar executar o htop &hellip; e olha só, reclama que não consegue localizar a biblioteca compartilhada libcurses e crasheia. Então vamos mover o ncurses de volta pro lugar com o comando <code>mv</code> inverso &hellip; e pronto, agora o htop consegue carregar como antes. Mas o exercício aqui foi pra vocês terem noção que um executável tem o que chamamos de dependências. E resolução de dependências é um problema recorrente que vai acompanhar sua vida inteira de programador. Quanto mais cedo entender sobre isso, melhor.</p>
<p>Agora, como instalar um software como htop? Simples, vai no terminal e digita <code>sudo apt install htop</code> e pronto. Mas você sabe o que tá acontecendo? Já parou pra pensar nisso? Como esse comando <code>apt</code> sabe de onde baixar e o que instalar no seu sistema? Deixa eu dar um resuminho. No caso de todo derivado de Debian, como um PopOS ou este Ubuntu, tudo começa no arquivo &ldquo;/etc/apt/sources.list&rdquo;, vamos dar <code>cat</code> pra ver o conteúdo. E temos uma lista com várias URLs. Ele já foi inteligente pra deixar pré-configurado domínios brasileiros, que são servidores espelho, ou seja, cópias dos servidores oficiais da Canonical, que ficam aqui no Brasil, pra ser mais rápido fazer download.</p>
<p>Quando você executa aquele comando que parece que não faz nada, o <code>apt update</code>, o que ele faz é baixar um zip de um desses servidores listados. Esse zip é um arquivo texto com a lista de todos os pacotes oficialmente suportados pela Canonical. O que faz um programador bom de verdade? Ele é curioso, se tem uma URL, a gente abre. Vamos abrir aqui essa primeira URL e carrega uma página com diretórios. Agora navegamos aqui em dists, que acho que é distribuições. E dentro temos os codenomes das distros de Ubuntu. Como mostra aqui no arquivo sources.list ele declara pra procurar o codenome &ldquo;jammy&rdquo;. Sabemos que Jammy Jellyfish é o codenome do Ubuntu 22.</p>
<p>Dentro temos o diretório &ldquo;main&rdquo;, depois &ldquo;binary-amd64&rdquo;, que são binários de 64-bits compatíveis com AMD ou Intel, então escolhemos ele e finalmente achamos um arquivo suspeito de 1.7 mega chamado &ldquo;Packages.gz&rdquo;. Vamos baixar. &ldquo;gz&rdquo; é a extensão de um arquivo compactado com &ldquo;gzip&rdquo;, então vamos pro terminal e fazemos <code>gunzip Packages.gz</code> e bingo, descompacta um arquivo de 6 mega que podemos abrir num editor de textos como meu vim.</p>
<p>Agora vamos procurar o htop que queremos, não é esse, não é esse outro, ah, achamos, olha só esse trecho. Várias informações sobre o htop, mas o que nos interessa é esse &ldquo;Filename&rdquo; aqui embaixo, que é o caminho pro pacote htop bla bla ponto deb. Mas onde fica esse diretório &ldquo;pool&rdquo;? Acho que já vi antes. Vamos voltando algumas páginas. Aha, olha só, tá aqui nesse servidor mesmo. A gente foi no &ldquo;dists&rdquo; lá em cima, mas tem esse &ldquo;pool&rdquo; aqui embaixo. Então vamos copiar a URL do servidor e ir pro terminal. Podemos usar a ferramenta <code>wget</code> pra facilitar, colando o endereço do servidor e copiando e colando esse caminho poll etc pro arquivo deb.</p>
<p>Pronto, baixamos o pacote do htop. Mas o que diabos é um arquivo ponto deb? Não é a mesma coisa mas é como se fosse um arquivo zip. Só que em vez de usar o comando &ldquo;gzip&rdquo; o padrão pra deb é usar o comando &ldquo;ar&rdquo;, que literalmente significa &ldquo;archive&rdquo;. Vamos fazer <code>ar x htop bla bla ponto deb</code>. Opção &ldquo;x&rdquo; significa extract. Olha só o que abriu, apareceu arquivos &ldquo;debian-library&rdquo; mas mais importante, temos esse &ldquo;control.tar.zst&rdquo; e &ldquo;data.tar.zst&rdquo;. Tar eu já expliquei no video de Ubuntu, é o comando de Tape Archive, que foi criado na época que se usava fitas magnéticas pra gravar arquivos.</p>
<p>Em resumo, o tar serve pra concatenar arquivos um atrás do outro num único linguição de bits, um único arquivão, pra mandar pra fita. É que nem um zip, mas sem necessariamente compactar. Mas nesse caso ele tá compactado, e de novo, não é um zip e sim um zstd. Se nunca ouviu falar, zstd é mais uma alternativa a gzip. Por ser mais moderno, usa algoritmos mais refinados de compressão e faz arquivos menores que gzip e é mais performático. Perfeito pra um gerenciador de pacotes.</p>
<p>Podemos descompactar fazendo <code>tar -I unzstd -xvf control.tar.zst</code> e bingo, ganhamos um arquivo chamado &ldquo;control&rdquo;. Podemos ver o conteúdo com o comando <code>cat</code> e olha só? Parece familiar? Sim, é exatamente o que tinha naquele arquivo Packages que baixamos antes. O arquivo Packages é montado automaticamente com o conteúdo desse arquivo control dos vários pacotes da distro. Agora descompactamos o outro arquivo com <code>tar -I unzstd -xvf data.tar.zst</code>. Esse é o arquivo que vai ter o binário executável do htop e outros arquivos que depende. Olha só, surgiu um diretório &ldquo;usr&rdquo; e vasculhando vemos que em &ldquo;usr/bin&rdquo; temos o binário do &ldquo;htop&rdquo;.</p>
<p>De curiosidade, naquele arquivo de control, veio também um arquivo chamado &ldquo;md5sum&rdquo;, vamos ver dentro. É uma lista de hashes md5 dos arquivos que tava no data.tar. Vamos checar? Só rodar <code>md5sum usr/bin/htop</code> e pronto, é exatamente o mesmo hash. Isso garante que o binário que temos aqui não foi corrompido nesse processo todo de download, descompressão. Lembra do episódio sobre detecção e correção de erros? Poderia ter um raio cósmico que flipou um bit, ou o meu HD poderia estar velho e falhado na hora de gravar um bit. Com essa checagem podemos garantir que o binário que está no meu disco tem 100% dos seus bits intactos.</p>
<p>Mas note uma coisa. Lembra que quando apagamos a biblioteca libncurses o htop não funciona? Cadê essa biblioteca? Não aparece nessa lista, se listamos os sub-diretórios que descomprimimos do data.tar, não existe &ldquo;lib&rdquo;, só &ldquo;usr&rdquo;. E é pra isso que serve aquele arquivo &ldquo;control&rdquo;. Vamos listar o conteúdo dele de novo e olhar com mais calma. Olha só como tem um item chamado &ldquo;Depends&rdquo;. Aqui mostra quais outros pacotes precisam ser instalados juntos e quais versões mínimas. Por baixo dos panos o comando <code>apt</code> vai também fazer esse mesmo processo que fizemos manualmente pra todos esses outros pacotes.</p>
<p>Se o apt não fosse inteligente pra fazer resolução dessas dependências, precisaríamos fazer na mão um comando bem maior, por exemplo <code>apt install htop libc6 libncursesw6 libnl-3-200, libnl-gen-3-200 libtinfo6</code>, mas como o apt sabe encontrar essa lista de dependências dentro do arquivo de &ldquo;control&rdquo;, dentro do pacote deb do htop, já instala automaticamente pra gente. A única coisa que resta é copiar os arquivos dentro desse diretório &ldquo;usr&rdquo; pra cima do diretório &ldquo;/usr&rdquo; do nosso HD, e é assim que o binário vai parar em &ldquo;/usr/bin/htop&rdquo;.</p>
<p>Em resumo bem resumido, essa é a anatomia de um pacote deb de Ubuntu. Um arquivão que dentro tem dois outros arquivões. Um é o data.tar que tem os binários do software. O outro é o control.tar que são metadados, informações como a descrição do software, autores, mantenedores, e dependências de outros pacotes. Num Fedora se usa pacotes em outro formato, o RPM que significa &ldquo;Red Hat Package Manager&rdquo; e foi criado na época das distros comerciais da Red Hat. Hoje o formato RPM ainda é usado pelo CentOS, Fedora, openSuse. As distros Arch Linux como Manjaro ou Garuda usam o formato PKG de pacote. E agora vamos voltar a falar do Slackware, que pacotes ele usa?</p>
<p>Na realidade, o Slackware original não usa nenhum formato especial de pacotes. Deixa eu mostrar o jeito antigo, o jeito padrão e o jeito moderno de instalar software num Slackware. Primeiro, vamos ver como anda a instalação. Olha só, ainda tá instalando como se não houvesse amanhã, é pacote pra caramba. O Slackware é disparado a instalação que mais ocupa espaço em disco comparado com qualquer outro. Quando terminar de instalar e baixar as atualizações todas, vai ocupar quase 20 gigabytes no disco.</p>
<p>Em comparação, o Ubuntu que instalei na versão mínima, sem softwares tipo Office, ocupa menos de 4 gigabytes. Mesmo se instalar os softwares opcionais, uma distro tipo Ubuntu ou Manjaro vai ocupar menos de 10 gigas, menos da metade do Slackware completo. E porque o Slack ocupa tanto espaço? Porque a filosofia é a seguinte: pra que ter uma ferramenta complicada tipo Apt ou Pacman pra ficar resolvendo dependências? Basta já ter tudo pré-instalado, todas as bibliotecas como aquela libncurses que o htop precisava, daí quando for instalar o htop, já vai ter o que precisa e não tem que se preocupar. E mesmo se não tiver tudo, dá pra instalar manualmente o pouco que falta.</p>
<p>Alguém poderia dizer &ldquo;porra, quer dizer que vai desperdiçar mais de 10 giga de coisas que talvez nem use?&rdquo;. Sim, vai mesmo. Se fosse uns 10 anos ou mais pra trás, essa reclamação teria mais peso, mas hoje em dia, 10 gigas a mais, 10 gigas a menos, meio que não faz diferença nenhuma. Com menos de 200 reais você compra um SSD barato de 240 gigabytes. Estamos literalmente falando de 10 real a mais ou 10 real a menos. Literalmente não faz diferença.</p>
<p>Especialmente se considerar que qualquer jogo Triple A no Steam, como um Red Dead Redemption 2 ou GTA 5 ocupam mais de 100 gigabytes. O remaster do Spider-man que saiu faz pouco tempo, um God of War ou Cyberpunk 2077 custam mais de 60 gigabytes cada. O que é 10 gigas a mais do Slackware? Nada. Merreca. Então, o argumento de desperdiçar espaço, em 2022, meio que tanto faz.</p>
<p>O problema é que mesmo tendo um monte de pacotes já pré-instalados, isso ainda não resolve o problema de dependências. A partir daqui vou adaptar o exemplo de um blog post que esbarrei 2 anos atrás. Vou deixar o link na descrição abaixo pra referência. O exercício é instalar a biblioteca &ldquo;jq&rdquo;, que é um processador de JSON de linha de comando. Quem mexe com APIs provavelmente já usou essa ferramenta, e se não usou, recomendo pesquisar depois.</p>
<p>Mas vamos lá, digamos que estamos nos anos 90, quando o conceito de gerenciador de pacotes ainda não era comum. Não temos APT nem Pacman nem nada disso. Não temos repositórios pra baixar pacotes. Como fazemos? Primeiro, procuramos o repositório de código. Antigamente estaria em sites como Sourceforge.net, hoje em dia provavelmente vai estar num GitHub. E de fato, aqui está a página. Agora vamos no link de releases e baixamos o tarball, que é o arquivo tar compactado com gzip do código fonte. Esse é o jeito antigo, nem existia Git nos anos 90, por isso não vamos usar Git.</p>
<p>Vamos pro terminal, diretório de downloads e fazemos o bom e velho <code>tar xvfz jq.tar.gz</code>. Entramos no diretório e olha só, temos o código fonte completo dessa ferramenta. E quem é das antigas já sabe o que tem que fazer. Rodar o bom e velho <code>./configure</code>. Todo código fonte de C costuma vir com um script executável chamado &ldquo;configure&rdquo;, que vai checar se temos todas as dependências de compilação nos lugares certos e gerar um arquivo de &ldquo;Makefile&rdquo; que rodamos com o comando &ldquo;make&rdquo; pra realmente pegar todo o código fonte, usar o compilador GCC e gerar o binário. Veja que o configure vai checando e em toda linha vai dizendo &ldquo;yes&rdquo;, &ldquo;yes&rdquo;, pra reportar que tá tudo ok. É isso que esperamos ver.</p>
<p>Mas antes do fim, erro. Fala que não achou o Oniguruma. Quem é experiente conhece o Oniguruma, que é uma biblioteca de Regex. Vamos listar o que tem nesse &ldquo;modules/oniguruma&rdquo; e, por coincidência, o autor desse projeto foi legal e já deixou copiado o código fonte do Oniguruma nesse diretório. Se não tivesse feito isso eu precisaria ir caçar o repositório do Oniguruma, baixar o tarball e descompactar aqui. Mas o erro aconteceu porque ele não achou o script de configure atualizado lá. E isso porque eu não li a documentação do README, como deveria. Se tivesse lido ia ver que o autor me manda rodar o comando <code>autoreconf</code> ANTES de rodar o <code>./configure</code>.</p>
<p>Autoreconf vai rodar outras ferramentas por baixo como autoconf, autoheader e mais. Quem é desenvolvedor de C e C++ conhece isso. Autoreconf vai gerar o script atualizado de configure. Então vamos rodar <code>autoreconf -fi</code> como manda. E agora sim, repetimos o <code>./configure</code>. Esperamos um pouquinho e pronto: agora sim, terminou sem problema. Significa que nosso Makefile tá pronto, então é só rodar o comando <code>make</code>. Pra quem é de Ruby o make é o avô de <code>rake</code>. Pra quem é de Javascript rodar make é tipo rodar <code>npm build</code> ou <code>yarn</code>. É um executador de tarefas. Ele vai automatizar rodar o GCC pra cada arquivo de código fonte C, depois pegar os objetos binário e linkeditar num binariozão executável.</p>
<p>E pronto, terminou de compilar. Se listarmos os arquivos, olha aqui o binário &ldquo;jq&rdquo;. Pra terminar de instalar precisa copiar esse binário pra um diretório do sistema como &ldquo;/usr/local/bin&rdquo;. Mas não precisa fazer manualmente, no Makefile tem uma tarefa chamada &ldquo;install&rdquo; justamente pra isso. Como precisa instalar em diretórios do sistema, o certo seria rodar com <code>sudo</code>, mas como é só um exemplo, eu não quero instalar de verdade. Vamos rodar <code>make install</code> sem sudo e olha só, dá erro, claro, porque não tem permissão pra copiar arquivos pra diretórios como &ldquo;/usr/local/lib&rdquo;. Ele tentou copiar a libonig que é a biblioteca do oniguruma que foi compilado junto.</p>
<p>Se executasse com <code>sudo</code>, iria copiar o libonig.so.4.0.0 pro /usr/local/lib e no final o jq pra /usr/local/bin. E pronto, finalizado. Era assim que a gente instalava software antigamente. Baixava o tarball com o código fonte, rodava direto <code>./configure</code> ou <code>autoreconf</code> antes pra gerar o script de configure. Checava se faltava alguma dependência. Caçava a dependência, baixava o tarball dele, e ia fazendo assim na munheca. Roda <code>./configure</code>. Compila tudo com <code>make</code>, instala os binários com <code>sudo make install</code>.</p>
<p>Antigamente isso de compilar tudo a partir do código fonte era mais necessário porque além de Intel x86 tinha vários tipos de CPUs diferentes. Tinha chips RISC Power da IBM. Chips Sparc de workstations Sun. PA-RISC da HP. DEC, Motorola. Fora a transição de 32-bits pra 64-bits. Como a comunidade de Linux era pequena, não tinha como ficar mantendo binários especializados de todas as versões pra todos esses chips. Era mais fácil fornecer o código fonte, todo mundo tinha compilador GCC, então cada um compilava o binário pra sua arquitetura de CPU.</p>
<p>Mas felizmente a gente evoluiu. Empresas como Red Hat apareceram e começaram a manter servidores com repositórios de binários já pré-compilados. E em vez de baixar código fonte, a gente passou a baixar pacotes RPM direto com os binários. Melhor ainda, como tinha um gerenciador de pacotes como o antigo Yum, dava pra manter um banco de dados com todos os pacotes instalados. Se quisesse desinstalar, tinha o manifesto com os arquivos de cada software, era só automatizar apagar. Por exemplo, pra apagar esse &ldquo;jq&rdquo; que acabamos de tentar instalar, antigamente, tinha que manualmente apagar o &ldquo;/usr/local/bin/jq&rdquo; e lembrar que também copiou arquivos &ldquo;libonig*&rdquo; em &ldquo;/usr/local/lib&rdquo;, e apagar manualmente. Sempre ficava coisa sobrando.</p>
<p>Hoje em dia, todo novo software povo já deixa preparado pra gerar pacotes como RPM ou DEB pelo menos. Daí um servidor automatizado baixa o tarball mais novo, compila os binários no servidor, e atualiza a lista de pacotes. É isso que empresas como Red Hat ou Canonical fazem pra manter suas distros. É pra isso que eles servem. O autor do jq se cadastra lá e fala: &ldquo;toda vez que sair versão nova, vocês podem baixar o tarball dessa URL aqui&rdquo;, e pronto. Daí em outros servidores eles montam a distro, rodam testes automatizados, e garantem que a versão nova dos pacotes não quebra nada. Se quebrar, dá rollback e descarta.</p>
<p>Vamos voltar pro Slackware. Como disse antes, não tem oficialmente nenhum equivalente de Apt ou Dnf pra gerenciar pacotes. No máximo tem scripts pra lidar com pacotes como pkgtool ou installpkg, que são bem simples. Ele assume que tudo que vamos precisar já tá instalado. Mas a realidade é que tem um monte de software que não vem na instalação. Esse &ldquo;jq&rdquo; é um exemplo.</p>
<p>Pra instalar software por fora, poderíamos fazer o que acabei de mostrar, que é baixar os tarballs, compilar e tals. Mas a comunidade Slack criou um site chamado Slackbuilds.org. Ele é um repositório que cadastra softwares como o &ldquo;jq&rdquo; e oferece um script em formato SlackBuild. No tal blog post que falei que vou usar de referência, em 2020 que é quando foi escrito, o jq declarava que tinha dependência com o oniguruma, como pode ver nessa foto de tela que tinha no blog.</p>
<p>Mas em 2022, se navegarmos pro site slackbuilds.org e pesquisar &ldquo;jq&rdquo;, vemos que não tem mais essa dependência. E já sabemos porque. Quando baixamos o tarball do jq, vimos que o autor já copia o codigo fonte do oniguruma junto. Daí não precisa mais compilar separado. O ponto do blog post era mostrar que pra instalar o jq, era necessário instalar o oniguruma separado na mão. Isso ainda é verdade pra vários outros softwares, mas por acaso pra esse exemplo não é mais verdade e menciono aqui justamente pra mostrar que blog posts envelhecem e software muda. Por isso você não pode ser um tapado que só vai copiando e colando, tem que saber o que tá acontecendo.</p>
<p>Agora sei que posso pular esse trecho todo do blog post e ir direto no slackbuild do jq. Eu tenho que baixar o código fonte original, que já fizemos agora pouco, e baixar esse jq.tar.gz que é o script pra gerar o pacote. Voltamos pro terminal, <code>tar xvfz jq.tar.gz</code>, entramos nesse diretório &ldquo;jq&rdquo; e movemos o tarball do código fonte aqui pra dentro. O segredo é esse script &ldquo;jq.SlackBuild&rdquo;. O que fazemos com ele? Vamos ver o que tem dentro ué.</p>
<p>É um scriptzão que configura variáveis de ambiente aqui no começo, acerta permissão dos arquivos e olha só, vai executar <code>./configure</code> com algumas opções a mais, rodar <code>make</code> pra compilar, depois <code>make check</code> que vai rodar os testes automatizados pra garantir que compilou certo. Pra você, babaca que acha que testes são desnecessários, o mundo open source só é estável como é hoje graças a testes automatizados. Versões mais novas do seu Ubuntu conseguem sair mais rápido, porque ele roda os testes de todos os pacotes antes de gerar a ISO que você faz download.</p>
<p>Finalmente o script roda aquele <code>make install</code>, mas manda instalar num sub-diretório, que é o que vai usar pra gerar o pacote final. Vamos executar aqui. <code>./jq.SlackBuild</code>. Só reclama que precisa de sudo, então repetimos com sudo. E pronto, no final fala que gerou um pacote jq bla bla ponto tgz. Pacote de SlackBuild é nada mais, nada menos que um zipão. E pra instalar, abrimos um shell de root com <code>sudo su</code>.</p>
<p>Aqui podemos rodar o script <code>installpkg /tmp/jq bla bla ponto tgz</code> e pronto, tá instalado. O &ldquo;jq&rdquo; roda bonitinho. Se dermos <code>which jq</code> fala que tá em &ldquo;/usr/bin&rdquo;. E a grande vantagem de instalar dessa forma em vez de fazer <code>sudo make install</code> na mão é que pra desinstalar fica mais fácil também. Vamos desinstalar. Só rodar <code>removepkg jq</code> e olha só, apagou todos os arquivos direitinho. Além do binário &ldquo;jq&rdquo; tinha arquivos de documentação, tinha libs, tudo que ia ficar pra trás sujando meu sistema se não tivesse uma forma automatizada de apagar.</p>
<p>E eu falei script, e não programa, porque installpkg é só um script de shell. Damos um <code>which installpkg</code> pra ver onde tá. Agora podemos fazer <code>cat /sbin/installpkg</code> e olha só. Só um script. Por exemplo, podemos ver que esse script suporta pacotes em 4 formatos diferentes de compressão. O nosso foi final tgz, mas ele suporta extensões como tbz, tlz e txz que respectivamente podem usar bzip, lzip ou xz em vez de gzip. Existem várias variações do lempel-ziv que zip usa, incluindo o zst que é o zstandard que o Debian usa em pacotes ponto deb. Se quiser aprender mais sobre scripts de shell, leia e tente entender esse script. Não é muito difícil.</p>
<p>Esses scripts installpkg e removepkg e pkgtool que eu não mostrei, são o equivalente à ferramenta dkpg do Debian e rpm dos derivados de RedHat. Um Apt usa dkpg por baixo. Um Dnf ou Zypper usa o rpm por baixo. Dpkg serve pra, dado que já tenho um arquivo ponto deb, ele sabe como abrir o pacote, como instalar e como desinstalar. O apt serve pra procurar e baixar o pacote, daí chama o dpkg pro resto.</p>
<p>Como falei antes, Slackware tem equivalente ao dkpg mas não tem equivalente ao Apt. Mas não significa que a única solução é ficar indo manualmente no site slackbuilds.org pra procurar os softwares que precisamos e gerar os pacotes manualmente. Já que o Slackware não tem nada oficial, a comunidade criou uma ferramenta, chamada de sbopkg. Sbo que significa SlackBuilds.org. Vamos instalar. No momento que estou escrevendo esse episódio, é a versão 0.38.2.</p>
<p>Só pegar o link, ir no terminal e baixar com wget. Agora entramos no shell de root e instalamos o pacote tgz com <code>installpkg</code>, como fizemos antes com o jq. E agora é só rodar <code>sbopkg -r</code>. Esse traço &ldquo;r&rdquo; vai fazer um rsync com o site slackbuilds.org pra ter uma cópia local dos pacotes disponíveis lá, pra possibilitar procurar pacotes sem ter que abrir o site num navegador. Por exemplo. Vamos instalar o mesmo &ldquo;jq&rdquo; usando essa ferramenta.</p>
<p>Ainda no shell de root podemos fazer <code>sbopkg -p jq</code>. Vai criar um arquivo em &ldquo;/var/lib/sbopkg/queues&rdquo;. Ele tem o conceito de fila, onde podemos ir enfileirando vários pacotes pra instalar de uma só vez. Mas aqui vamos direto já rodar <code>sbopkg -B -i jq.sqf</code> e pronto. Tá instalado. Se fosse um pacote com várias dependências, não precisaríamos instalar uma por uma na mão como antes, o sbopkg vai se encarregar de instalar tudo sozinho. Vamos instalar algo mais complexo, como um neovim, mas um pouco diferente agora. Digite só o comando <code>sbopkg</code> sem nenhum parâmetro e olha só, abre uma interface interativa em ncurses.</p>
<p>Podemos dar search pra achar o neovim, selecionar o pacote que quero, e dar install. E pronto. Ele se vira. É tipo a loja de software do seu Ubuntu só que em modo texto. Tem opções pra desinstalar, pra atualizar todos os pacotes e mais. Com isso o slackware também fica um pouco mais moderno e parecido com outras distros.</p>
<p>E a quantas anda nossa instalação de Slackware? Vamos ver, puts, continua copiando pacotes. Eu vou parar por aqui, porque no final a única coisa que vai sobrar é ele me perguntando que gerenciador de janelas queremos usar, se queremos KDE ou XFCE mas a curiosidade é que ele vem também instalado com o antigo Window Maker, que é a base da interface de MacOS desde a primeira versão &ldquo;X&rdquo; ou &ldquo;10&rdquo;, a versão open source do que vinha nos computadores da Next do Steve Jobs. E também vem com Motif, que é interface gráfica que vinha em workstations UNIX como da Sun ou Silicon Graphics. Vale testar de curiosidade, mas são bem rudimentares. No final escolhe um KDE da vida que tá de bom tamanho. Reboota e pronto, tá igual qualquer outra distro.</p>
<p>O Slackware é provavelmente a distro mais antiga ainda em atividade hoje e eu escolhi falar dela pela oportunidade de explicar vários conceitos que acontecem por baixo dos panos, escondido, em distros mais modernas, e que vocês dificilmente iam aprender sozinhos. Eu sei tudo isso porque comecei literalmente usando Slackware e Red Hat nos anos 90 quando nenhuma das distros modernas existia. Ubuntu só viria a aparecer lá por 2005 ou 2006. Arch só aparece depois de 2001. Todas as distros derivadas de Ubuntu como PopOS só aparecem depois de 2010.</p>
<p>Pra mim sempre tudo fez sentido justamente porque aprendi numa época em que a única coisa que a gente tinha era um tarball de código fonte e o GCC pra compilar e mais nada. Por isso foi mais fácil pra mim do que é pra vocês. É muito mais difícil entender as ferramentas de hoje porque tem misturado ferramentas do passado e é confuso saber o que é pra usar quando. Um usuário novato que escolher Slackware vai bater cabeça sem saber como instalar software, porque não entende a filosofia de tarballs e ia ter dificuldade de entender a diferença do script <code>installpkg</code> pro projeto <code>sbopkg</code>, sem saber que o primeiro é fundação pro segundo.</p>
<p>De qualquer forma, eu acho interessante a idéia de já ter um monte de coisa pré-instalada. Espaço hoje em dia é barato. E mesmo assim ele não fica pesado porque apesar de estar instalado, nada que você não pedir vai carregar. Por exemplo, se já tiver apache instalado mas eu não habilitar o serviço pra carregar, então não vai consumir memória ou CPU a mais só por estar lá. Porém, do ponto de vista de segurança eu não gosto. Em segurança queremos sempre ter o mínimo possível de software pré-instalado, porque não dá pra saber se um software antigo, sem manutenção, não tem um bug de segurança que pode ser usado pra algum hacker escalar privilégios pra root e tomar conta da nossa máquina. Eu recomendo sempre só ter instalado o que realmente precisa usar.</p>
<p>De qualquer forma, vale tentar instalar e usar por um tempo, pra aprender mais uma distro diferente de Linux. Tendo um sbopkg, o uso é parecido com um apt de Ubuntu ou dnf de Fedora, então não deve dar muito trabalho de usar no dia a dia. Muitos de vocês já instalaram dezenas de software num Ubuntu da vida, sem saber que tava baixando pacotes DEB, sem saber que esses pacotes nada mais são que um zipão. No próximo episódio vamos falar mais conceitos de Linux. Se ficaram com dúvidas, mandem nos comentários abaixo. Se curtiram o video deixem um joinha, assinem o canal pra não perder a continuação, e compartilhem o video com seus amigos. A gente se vê, até mais!</p>
]]></content:encoded><category>linux</category><category>tutoriais</category><category>akitando</category></item><item><title>[Akitando] #127 - Como Funciona o Boot de um Linux? | O que tem num LiveCD?</title><link>https://akitaonrails.github.io/2022/09/12/akitando-127-como-funciona-o-boot-de-um-linux-o-que-tem-num-livecd/</link><guid isPermaLink="true">https://akitaonrails.github.io/2022/09/12/akitando-127-como-funciona-o-boot-de-um-linux-o-que-tem-num-livecd/</guid><pubDate>Mon, 12 Sep 2022 18:05:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/5F6BbhgvFOE"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Instalar distros Linux hoje em dia é muito fácil. Só bootar do pendrive, clicar em &amp;ldquo;próximo&amp;rdquo;, &amp;ldquo;próximo&amp;rdquo; e pronto, tá tudo instalado e funcionando. Qualquer um consegue instalar na maioria das configurações modernas de hardware.&lt;/p&gt;
&lt;p&gt;Porém, isso não te ensina nada e ao final você continua não sabendo absolutamente nada sobre o que é um Linux e o que de fato ele faz. Como é o processo de boot? Quais softwares estão envolvidos nesse processo? E mais importante: o que posso fazer quando alguma coisa dá errado? Quais ferramentas tenho à minha disposição?&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/5F6BbhgvFOE"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>Instalar distros Linux hoje em dia é muito fácil. Só bootar do pendrive, clicar em &ldquo;próximo&rdquo;, &ldquo;próximo&rdquo; e pronto, tá tudo instalado e funcionando. Qualquer um consegue instalar na maioria das configurações modernas de hardware.</p>
<p>Porém, isso não te ensina nada e ao final você continua não sabendo absolutamente nada sobre o que é um Linux e o que de fato ele faz. Como é o processo de boot? Quais softwares estão envolvidos nesse processo? E mais importante: o que posso fazer quando alguma coisa dá errado? Quais ferramentas tenho à minha disposição?</p>
<p>Hoje é o dia de finalmente você começar a entender como sua máquina realmente funciona.</p>
<p>== Conteúdo</p>
<ul>
<li>00:00 - Intro</li>
<li>01:11 - CAP 1 - Instalar Linux é Fácil | Ubuntu 22.04</li>
<li>06:11 - CAP 2 - O que é um Mount Point? | Tudo em Linux são Arquivos</li>
<li>11:45 - CAP 3 - Firmwares e UEFI | Quem começa o boot?</li>
<li>15:43 - CAP 4 - O que é uma “imagem” ou ISO? | O que tem num LiveCD?</li>
<li>26:03 - CAP 5 - O “meta-Linux” antes do Linux | Init RAM</li>
<li>31:26 - CAP 6 - Depois do Linux: Daemons | Systemd</li>
<li>33:31 - CAP 7 - O que são “Run Levels”? | O jeito SysV antigo</li>
<li>40:40 - CAP 8 - O fim do Init SysV? | O jeito novo do Systemd</li>
<li>45:04 - Bloopers</li>
</ul>
<p>== Links</p>
<ul>
<li>gnome-cedilla-fix (<a href="https://github.com/marcopaganini/gnome-cedilla-fix"target="_blank" rel="noopener">https://github.com/marcopaganini/gnome-cedilla-fix</a>)</li>
</ul>
<p>== SCRIPT</p>
<p>Olá pessoal, Fabio Akita</p>
<p>Por hora, finalizei finalmente a minissérie sobre redes e internet e agora é hora de falar um pouco mais sobre Linux  pra complementar os videos de Ubuntu e WSL que já fiz aqui no canal. Tem uma playlist de Software Livre onde falo de assuntos relacionados, caso ainda não tenha assistido e vou adicionar os novos videos nessa mesma playlist. Nos próximos videos vou mostrar um pouco mais em detalhes sobre instalação de algumas distros consideradas mais exóticas, mas hoje quero explorar mais conceitos de Linux e software em geral que talvez vocês não conheçam usando como desculpa a explicação de como um Linux faz boot. Vamos começar a olhar um pouco mais debaixo do capô.</p>
<p>(&hellip;)</p>
<p>Pra escolher distros, hoje em dia é fácil. Basta ir em canais de reviews que tem de tonelada, como do camarada DioLinux ou outros que vem crescendo como do Chris Tut. O comportamento padrão vai ser você vendo qual tem o visual que mais te atrai e daí baixar um Deepin ou PopOS da vida, ou se quiser se sentir mais &ldquo;edgy&rdquo; e &ldquo;gamer hipster&rdquo; da vida, um Garuda. É assim que 99% de todo mundo que usa Linux faz pra escolher.</p>
<p>Além disso, a instalação de qualquer distro tá super trivial. Você baixa o arquivo de ISO no Windows mesmo, usa um programa como o Rufus pra gravar essa ISO num pendrive. Faz boot pelo pendrive e boom, tem um ambiente ao vivo de Linux já funcionando. Ele detecta sozinho praticamente todo o seu hardware. Se nesse ambiente do pendrive parecer que tá tudo funcionando, daí tem um ícone de instalação. Vai abrir o Calamares, que é o programa de instalação que quase toda distro moderna usa. Por isso mesmo meio que tanto faz qual distro escolher, o processo de instalação é muito parecido.</p>
<p>O passo a passo que o Calamares abre é sempre o mesmo. Começa pedindo pra escolher que língua usa e que layout de teclado prefere. Eu sempre escolho inglês e o layout US international que, até hoje, continua com o mesmo problema de não suportar acento agudo no &ldquo;c&rdquo; pra gerar cedilha, mas isso é fácil de consertar com um script que se acha fácil no Google. No caso do Ubuntu, pede pra escolher se quer instalar um monte de aplicativos como LibreOffice, Gimp, aplicativo de fotos e coisas assim, ou uma instalação mínima, que é o que eu prefiro. Eu gosto de já escolher pra baixar os pacotes mais atualizados via internet e também instalar software proprietário de terceiros, como codecs multimídia e drivers de placa de video da NVIDIA.</p>
<p>Se você é iniciante, nem dá bola pra isso, mas no mundo de software livre é uma discussão importante. Por causa de licença de software, toda distro faz um esforço pra ter por padrão somente o que é de verdade software livre, com licenças como GPL ou BSD. Porém, muitos dispositivos, em particular a NVIDIA, não libera o código fonte dos drivers, somente o binário fechado. Mas se você instala isso no seu PC, está concordando com a licença restritiva da NVIDIA e contaminando seu ambiente com software fechado. Do ponto de vista filosófico, não é recomendado. Mas pra nós, usuários finais, meio que tanto faz isso. Por isso sempre habilito, porque eu não sou ativista de software livre. E antes que você vá nos comentários pra dizer que viu notícia que a NVIDIA liberou código dos drivers, nem perca tempo, pra variar é matéria de jornalista bosta que não sabe ler e não viu que eles liberaram só uma pequena parte do código só pra marketing. A parte importante continua fechada e, mais importante, com licença restritiva de uso.</p>
<p>Enfim, o próximo passo costuma ser pra escolher em qual SSD ou HD quer instalar, como quer criar partições e com qual sistema de arquivos. O Calamares costuma ter essa primeira opção que diz o seguinte &ldquo;eu sei que você não tem idéia do que tá fazendo, então deixa que o papai aqui cuida de tudo pra você&rdquo;. É a opção pra idiotas, quero dizer, pessoas não-técnicas. Ele vai apagar o disco todo e criar as partições automaticamente pra você. No caso do Ubuntu, recomendo pelo menos tentar instalar com o sistema de arquivos ZFS em vez do ext4. Se você não tem idéia do que é isso, não esqueça que eu fiz uma minissérie inteira explicando o que são partições, volumes, como o PC faz boot via EFI, e o que diabos é ext4 ou ZFS. Então vai assistir depois.</p>
<p>Finalmente, o Calamares vai mandar preencher um formulário com seu nome e que usuário e senha quer cadastrar pra poder fazer login depois. Obviamente você deveria escolher uma senha aleatória e forte e não &ldquo;senha123&rdquo; ou &ldquo;teste123&rdquo;. Também te dá a opção de criar uma senha diferente pro usuário &ldquo;root&rdquo; que é o administrador da máquina. Mas aqui estou escolhendo a opção preguiçosa que é usar a mesma pros dois usuários e fazer login automático porque só eu vou usar essa máquina mesmo.</p>
<p>E pronto. Acabou. Agora o Calamares vai copiar os arquivos do pendrive pra partição que acabou de criar. Vamos acelerar porque essa é a parte que demora mais, de 15 minutos a meia hora dependendo do seu PC. No final ele configura a carga do boot, e agora é só reiniciar e tá tudo instalado. É absurdamente fácil instalar qualquer distro Linux que usa Calamares hoje em dia. Olha só, vou acelerar aqui o boot, tirar o pendrive, e pronto. Um Ubuntu quentinho recém saído do forno, com tudo funcionando. Se seu PC não for muito bosta, já vai estar tudo detectado, placa de vídeo, som, seu teclado, mouse, wifi e tudo mais. Não precisa fazer mais nada.</p>
<p>Muita gente acha o Ubuntu feio, e eu não discordo muito. Apesar que eles tem melhorado o tema visual e até que hoje, pra mim, não cheira nem fede. Não teria nenhum problema de usar um Ubuntu sem mudar muita coisa. A partir daqui, se você nunca mexeu com Linux antes, veja primeiro o meu video só sobre Ubuntu onde também explico os comandos e conceitos mais básicos que vou assumir que todo mundo já sabe pra continuar este video.</p>
<p>Se escolher um Linux Mint, Elementary, Budgie, Zorin ou PopOS, o funcionamento não vai diferir porque todos são derivados de Ubuntu. Por isso que muito tutorial que funciona no Ubuntu vai funcionar neles também. Cada um tem alguma característica pra se diferenciar. Seja ter um gerenciador de janelas customizado diferente, seja já ter coisas pré-instaladas e pré-configuradas pro usuário não ter que configurar manualmente depois. Mas na prática, pra todos os efeitos e propósitos, é quase a mesma coisa que usar direto Ubuntu.</p>
<p>Antes de continuar, vamos olhar o que fizemos até agora. Um bom programador precisa se acostumar a fazer essas perguntas. Você baixou um arquivo de ISO, gravou num pendrive, e ele logou uma versão light de Ubuntu, sem nem ter instalado nada. Como isso é possível, já parou pra se perguntar isso? Vai, pensa alguns segundos. Como que faz pra bootar um Linux de um pendrive a partir de um mero arquivão com extensão ponto ISO no final?</p>
<p>Se você assistiu minha minissérie sobre armazenamento e sistemas de arquivos talvez já saiba parte da resposta. Deixa eu aproveitar pra mostrar alguns conceitos de Linux pra vocês. Um dos fundamentos inventados no UNIX original e herdado pelo Linux é a idéia de tentar fazer tudo num computador ser representado como se fossem diretórios e arquivos. Por exemplo, talvez já tenha usado o comando <code>ps</code> pra listar os programas rodando. Só <code>ps</code> sem nada mostra os processos que seu usuário iniciou, mas se usar opções como <code>ps aux</code> podemos ver todos os processos que foram iniciados desde o boot pelo usuário <code>root</code> também.</p>
<p>Já expliquei em outros videos sobre processos e como eles tem um PID, que é um process ID, um número que identifica um processo e como podemos mandar sinais sigterm, como <code>kill -9 pid</code> pra matar um processo forçadamente. Agora, lembrem que todo Linux costuma ter um diretório na raíz chamado <code>/proc</code>. Posso fazer <code>ls /proc/pid</code> e olha só, aparece um monte de arquivos e diretórios falsos. O que tem dentro desse diretório não são arquivos de verdade, são informações sobre esse processo representado como arquivos. Por exemplo, posso usar um comando normal de arquivos como o <code>cat</code> que se usa pra listar o conteúdo de um arquivo texto.</p>
<p>Fazendo <code>cat /proc/pid/status</code> temos várias informações sobre esse processo, como quanto de memória ele tá usando. Se eu quiser fazer uma ferramenta parecida com <code>ps, top ou htop</code> da vida, posso consultar as informações de cada processo nessa árvore embaixo de <code>/proc</code>. Fica de exercício pra vocês fazer uma versão parecida com o comando top ou htop, mas usando python ou javascript ou o que quiser, só lendo do diretório /proc, sem usar syscalls pra kernel. Esse diretório não tem arquivos de verdade, só representações virtuais. Quando der boot, esse diretório desaparece. Se você remover esse HD e ligar em outro PC como HD externo USB por exemplo, não vai ter nada dentro.</p>
<p>Esse diretório é o que chamamos de ponto de montagem, um mount point. Tente digitar o comando <code>mount</code> no terminal. Vai aparecer uma lista como essa. Esses são os pontos de montagem ativos atualmente. E que diabos é um ponto de montagem? Num Windows pontos de montagem costumam ser letras como C:, D:, E:. Em Linux são sub-diretórios. Pra explicar isso você precisa entender que, uma coisa é o hardware de armazenamento como um HD ou pendrive. Outra coisa é como o sistema operacional acessa esse hardware.</p>
<p>O que é um dispositivo de armazenamento? Cansei de repetir em vários videos que são dispositivos que gerenciam blocos de bits, ou block devices. Seja um CD, um pendrive USB ou um SSD, todos são dispositivos de bloco. E por acaso, num Linux temos comandos como <code>ls</code> que a essa altura você já sabe que significa <code>listar</code> e <code>blk</code> ou seja listar block devices. E agora sim temos os nomes dos dispositivos plugados no meu PC, por exemplo, o HD principal se chama <code>sda</code>. Um segundo HD se chama <code>sdb</code>, um pendrive, quando plugar vai aparecer como <code>sdc</code> ou <code>sde</code>.</p>
<p>Além de <code>/proc</code> existe outro ponto de montagem que toda distro Linux monta automaticamente no boot, que é o <code>/dev</code>. De novo, o que tem dentro não são diretórios nem arquivos de verdade. Ele representa os dispositivos de armazenamentol. Por isso em muitos tutoriais por aí você já deve ter esbarrado num <code>/dev/sda</code> da vida. Isso não é um arquivo, é um canal pra falar com o HD físico “via” um arquivo virtual. No Windows funciona assim também, o HD principal costuma ter o nome de <code>\\.\PhysicalDisk1</code>. Abre um Powershell no Windows e digita <code>Get-PhysicalDisk</code>. Por exemplo, esse meu NVME Samsung 970 EVO Plus de 2TB se chama PhysicalDisk2. Num Linux um nome equivalente seria <code>/dev/sdc</code>.</p>
<p>Beleza, o Linux tá enxergando os dispositivos, mas agora como navego dentro desses dispositivos? Agora eu preciso de um mount point. O ponto de montagem é justamente dizer que a partição 1 do drive <code>/dev/sda</code> vai ser o diretório raíz &ldquo;/&rdquo; e é pra interpretar os bits desse canal usando o sistema de arquivos ext4. Leia o arquivo &ldquo;/etc/fstab&rdquo; que significa tabela de file system. Em cada linha vai ter dizendo a partição do disco, o ponto de montagem, o sistema de arquivo e opções, por exemplo, pra abrir só pra leitura, caso seja um CD-ROM.</p>
<p>Quando espeto um pendrive nesse meu Ubuntu, automaticamente aparece aqui no meu desktop. Isso porque tem um serviço rodando em background que vai rodar pra mim o comando <code>mount -t vfat /dev/sdc /run/media/akitaonrails/pendrive</code>, por exemplo. Em distros mais antigas, eu precisaria rodar esse comando manualmente no terminal se quisesse acessar o pendrive. E o equivalente a dar um eject é rodar o comando <code>umount /run/media/akitaonrails/pendrive</code>.</p>
<p>Sacaram? Montar é dizer pro sistema operacional como esse dispositivo foi formatado e onde quero que ele apareça e com qual sistema de arquivos é pra interpretar os bits dentro. No Windows isso é feito em aplicativos com o Disk Management, onde posso mudar o ponto de montagem, que no caso é só uma letra de drive, como E:.</p>
<p>Vamos recapitular o que já expliquei nos videos sobre volumes e partições. Num computador moderno, logo que liga, quem vai carregar primeiro é um software chamado firmware, que muitos ainda chamam erroneamente de BIOS. Mas é o software que não está no HD e sim num chip de memória na placa mãe. Por isso que mesmo num PC sem HD nenhum, se você ficar apertando uma tecla como &ldquo;delete&rdquo; várias vezes, entra naquele software de configuração, onde pode atualizar a data e outras configurações do sistema.</p>
<p>Antigamente esse software era chamado BIOS mesmo, que significa Basic Input/Output System, ou sistema básico de entrada e saída que literalmente tem o básico só pra conseguir identificar seu monitor, teclado e mouse pra você conseguir interagir com o computador antes de tentar carregar o sistema operacional do disco. Mas BIOS era um sistema básico até demais, que só conseguia lidar com identificadores de 16-bits, o que impossibilita usar HDs com mais do que 4 partições. Nos anos 90 isso já era pouco e representava um problema.</p>
<p>Por isso hoje todo PC moderno tem um firmware UEFI que significa Universal Extensible Firmware Interface. UEFI é um padrão pra firmwares. Além disso antigamente partições eram organizadas com MBR ou Master Boot Record, que é o registro mestre de boot no HD, que são os bits de boot do sistema operacional que a BIOS vai tentar executar logo na sequência. Hoje em dia é UEFI e GPT que é o GUID Partition Table. Eu falei que antigamente estávamos limitados a 4 partições com identificadores de 16-bits. Em GPT, em vez de números, usamos GUIDs que é uma sequência de 32-bits. Números parecidos com esse aqui que você já deve ter visto. Parece aleatório e não são sequenciais. E com isso hoje podemos ter quantas partições de quantos tipos quisermos. Expliquei tudo isso em mais detalhes no video de tudo que você queria saber sobre dispositivos de armazenamento, depois assiste lá.</p>
<p>Gerenciadores de boot como LILO ou Grub foram criados pra conseguir lidar com as limitações de BIOS e MBR. Com UEFI e GPT nem precisa mais deles, o próprio firmware tem capacidade pra fazer boot de qualquer partição GPT, basta ter o bootloader numa partição que o UEFI consiga ler, formatado em fat32, por exemplo. E isso me faz voltar aqui pro nosso Linux de exemplo. Vamos ver o arquivo &ldquo;/etc/fstab&rdquo; de novo. Olha só, tem uma partição <code>/dev/sda1</code> cujo ponto de montagem é &ldquo;/boot&rdquo;. E de um terminal normal mesmo, usando comandos mundanos como &ldquo;ls&rdquo; podemos ver que tem um diretório chamado <code>/boot/efi</code> e é aqui que fica o bootloader, ou literalmente o carregador de boot que o firmware UEFI vai carregar depois do POST.</p>
<p>É nesse ponto que o firmware UEFI pára de executar e dá controle pro bootloader. O bootloader é quem vai de fato carregar a kernel do sistema operacional, no caso o Linux. E é logo um diretório pra trás, em &ldquo;/boot&rdquo; que está o Linux de verdade. É um binário executável chamado “vmlinuz” até que pequeno, de pouco mais de 10 megabytes, já compilado com os principais drivers que precisa pra iniciar o sistema, como driver pra ler HD e abrir a partição formatada nele, onde vai estar o resto do sistema operacional. O bootloader vai se encarregar de executar o binário e é nesse estágio que a tal kernel do Linux finalmente vai começar a exercer controle sobre a máquina.</p>
<p>A kernel vai começar a carregar drivers pra ter acesso à memória, aos discos, a periféricos como teclados, monitor e tudo mais. Uma vez que agora a kernel tem controle, ela vai criar um espaço em memória com uma partição virtual, usando um initramfs, literalmente file system de ram de inicialização. Sabe seus diretórios de Linux na raíz do seu HD? Pensa uma versão menor disso, mas montado em RAM. Esse sistema de arquivos estava comprimido junto com a kernel naquele arquivo em &ldquo;/boot&rdquo;, é esse outro arquivo chamado “initrd.img”, veja que ele é bem maior com uns 100 megabytes. Mas deixa eu fazer uma tangente pra ficar mais claro.</p>
<p>Vamos abrir o terminal de novo. Existe uma ferramenta que costumamos usar pra fazer clones de HDs ou pendrives no nível dos blocos, independente se tá formatado em ext4, ntfs ou fat32. É o comando &ldquo;dd&rdquo;. E o que significa &ldquo;dd&rdquo;? Significa Copy and Convert .. eh, deveria ser &ldquo;cc&rdquo; só que CC em Linux já significa &ldquo;C Compiler&rdquo;, então escolheram a letra seguinte .. eh, programador é uma bosta pra dar nome pras coisas. Mas enfim, dd funciona assim, tem um parâmetro &ldquo;if&rdquo; que não sei se era &ldquo;input file&rdquo;, é onde coloco o caminho do dispositivo que quero copiar, tipo &ldquo;/dev/sdc&rdquo; e no parâmetro &ldquo;of&rdquo; que talvez seja &ldquo;output file&rdquo; coloco o path de pra onde quero copiar.</p>
<p>Aquele programa de Windows chamado Rufus? É mais ou menos a versão gráfica desse comando. Por exemplo, se do terminal de um Linux eu quiser gravar aquele arquivo de ISO no meu pendrive, poderia ter feito <code>dd if=ubuntu.iso of=/dev/sdc</code> e pronto. Lógico, tenho que garantir que “/dev/sdc” realmente é meu pendrive, porque se eu confundir com uma partição do meu HD, esse comando vai gravar a ISO por cima e já era meus arquivos que tavam lá. Agora, só pra complicar deixa mostrar mais alguns truques de Linux que talvez vocês não saibam. Naquele ponto de montagem &ldquo;/dev&rdquo; que tem representado dispositivos como meus HDs ou pendrives, também temos alguns dispositivos virtuais especiais como &ldquo;/dev/null&rdquo; ou &ldquo;/dev/urandom&rdquo;.</p>
<p>De novo, podemos usar comandos como <code>cat</code> com eles. Vamos fazer <code>cat /dev/null</code> e, uau, nada. Como o próprio nome diz, esse dispositivo devolve nulo. Mas muitos scripts gostam de usar ele pra mandar coisas que quero jogar no lixo. Por exemplo, digamos que eu rode um comando que imprima várias informações na tela que não estou interessado. Posso redirecionar o stdout do comando pra /dev/null pra ele ser engolido e mandado pro limbo. Um exemplo besta é o comando &ldquo;echo&rdquo; que só imprime na tela. Coloco um sinal de &ldquo;maior&rdquo; pra redirecionar pra &ldquo;/dev/null&rdquo; e pronto, engoliu o saída. Se começar a fuçar scripts de shell, vai esbarrar mais nisso.</p>
<p>O &ldquo;/dev/urandom&rdquo; por outro lado é um dispositivo que cospe bits aleatórios. Muitas funções de muitas linguagens, incluindo as funções da kernel do Linux e bibliotecas de criptografia usam esse dispositivo quando precisam gerar números aleatórios. Tecnicamente, isso é um PRNG ou pseudo random number generator. É um gerador de números pseudo aleatórios. Em computador não existe aleatoriedade verdadeira, só uma aproximação. Teríamos que explorar conceitos como entropia e tudo mais pra justificar isso, mas não é objetivo de hoje, só entenda que existe isso.</p>
<p>Pra hoje, mais importante é outro dispositivo virtual, o &ldquo;/dev/zero&rdquo;. Assim como o &ldquo;/dev/null&rdquo; se eu redirecionar a saída stdout de qualquer comando pra ele, vai engolir tudo. Por outro lado se der um <code>cat</code> ou ficar lendo dele, vou receber literalmente nada, só caracteres nulos. Vai cuspindo nulos infinitamente até eu forçar a parada com Ctrl+C. E pra que diabos isso serve? Porque posso usar numa outra ferramenta que aceita dispositivos como parâmetro, como nossa ferramenta &ldquo;dd&rdquo;.</p>
<p>E se em vez de ler de um arquivo de ISO, eu ler desse dispositivo &ldquo;/dev/zero&rdquo;? Vamos voltar ao comando &ldquo;dd&rdquo; e fazer <code>dd if=/dev/zero of=hello.img bs=1M count=10</code> que significa, leia bits do dispositivo &ldquo;/dev/zero&rdquo; e grave num arquivo chamado &ldquo;hello.img&rdquo; usando blocos de 1 megabyte de tamanho e contando até 10, ou seja, até gerar um arquivo de 10 megabytes. Pronto. Criei um arquivo cujo conteúdo é só de zeros. Olha só, <code>ls -la</code> e o arquivo tem mesmo 10 megabytes. Se usar o comando <code>cat</code>, não imprime nada, porque dentro é tudo bits vazios. E mesmo se usar um comando que lê binários como <code>hexdump</code>, ele mostra que os primeiros bits são todos 0 e esse asterisco significa que todo o resto é igualzinho à primeira linha até o endereço 10 megabytes.</p>
<p>E pra que diabos preciso de um arquivo totalmente vazio ocupando espaço com bits zero? Bom, em Linux temos comandos pra formatar partições do seu HD como <code>mkfs.ext4</code> ou <code>mkfs.vfat</code> onde &ldquo;mkfs&rdquo; literalmente quer dizer &ldquo;make file system&rdquo;. Sabe no Windows quando espeta um pendrive, vai no explorar em cima da letra do drive e com o botão direito do mouse escolhe a opção de &ldquo;formatar&rdquo;? É a mesma coisa só que na linha de comando de Linux. Vamos formatar esse arquivo então.</p>
<p>No caso, vou formatar com o <code>mkfs.vfat</code> pra formatar em FAT32, como eu faria com um pendrive que gostaria de conseguir ler independente de que sistema operacional espetar ele. Só fazer <code>mkfs.vfat -F 32 hello.img</code>. Não sei se vocês estão conseguindo entender, normalmente se usa um comando como <code>mkfs.vfat</code> na partição de um dispositivo hardware de verdade, como em &ldquo;/dev/sda1&rdquo;, mas dá pra formatar dentro de um arquivo. E lembra como usando comandos como <code>cat</code> ou mesmo <code>hexdump</code> só voltava vazio? E agora? O que tem nesse arquivo? Vamos ver.</p>
<p>E temos o cabeçalho de uma partição FAT32. Olha só do lado direito o que ele reconhece como caracteres ASCII com mensagens de erro e dizendo que é FAT32. Essa é exatamente a mesma sequência de bytes que encontraríamos lendo direto de um pendrive. Vamos ver? Vamos plugar o pendrive e fazer o mesmo comando de &ldquo;hexdump&rdquo; direto do &ldquo;/dev/sdb1&rdquo; e olha só, esse é o pendrive. São os mesmos tipos de bytes. E já sabemos que a partir de um &ldquo;/dev/sdb1&rdquo; podemos montar num diretório como &ldquo;/run/media&rdquo; da vida. E esse arquivo que acabamos de formatar, dá pra montar também?</p>
<p>Basta criar um diretório qualquer como <code>mkdir hello</code> e fazer <code>mount -t vfat hello.img hello</code>. Podemos dar <code>cd hello</code> e agora estamos DENTRO do arquivo hello.img. Sacaram? Deixa eu criar um arquivo texto qualquer chamado &ldquo;blabla.txt&rdquo; com qualquer coisa dentro e salvar. Vamos listar e olha só o arquivo. Agora vamos sair desse diretório com &ldquo;cd ..&rdquo; e desmontar isso. Só fazer <code>umount hello</code>. Se tentarmos listar o conteúdo do diretório hello agora, não vai ter nada, porque o arquivo blabla ficou dentro do hello.img que acabamos de ejetar, como se fosse um pendrive que desconectamos. O arquivo blabla não foi criado dentro desse diretório. O diretório só tava servindo como ponto de montagem PRA DENTRO do hello.img.</p>
<p>Se eu fizer um hexdump no hello.img olha aqui no fim, os bytes do arquivo que criamos tá dentro dele. Este arquivo hello.img, do ponto de vista do sistema operacional é a mesma coisa que um pendrive, só que sem a parte hardware, só os bits, o linguição de bits. Se ficou confuso, vamos repetir. Lembra o arquivo de ISO que baixamos do site do Ubuntu e gravamos no pendrive? É um arquivão que chamamos de “imagem”. Vamos montar aqui no mesmo diretório hello. Só fazer <code>mount -t iso9660 -o ro ubuntu.iso hello</code>. No caso, um CD-ROM é formatado no padrão iso9660, não é fat nem ext4. E coloquei a opção de &ldquo;ro&rdquo; porque CD-ROM é read-only, só de leitura, não dá pra modificar nada dentro.</p>
<p>Vamos listar o que tem em hello e olha só, todos os arquivos que estão dentro do ISO. É isso que é gravado pelo Rufus num pendrive que queremos dar boot de Ubuntu. Tem o diretório &ldquo;boot&rdquo; com o bootloader, o kernel e tudo mais que precisa pra conseguir bootar. É isso que é um LiveCD. Mas o objetivo de toda essa história é pra vocês começarem a largar noções que existe uma entidade física chamada &ldquo;dispositivo&rdquo;, como um HD ou pendrive, e entender que disposivo de bloco é literalmente qualquer sequência de bits, qualquer linguição de bits, como um arquivo, ou uma conexão de rede. Basta ser formatado de uma forma que dê pra montar com o comando <code>mount</code>.</p>
<p>O sistema operacional não tem preconceitos. Pra ele foda-se se você espetou um pendrive ou se montou um arquivão de imagem. Pra ele só interessa: eu consigo ler e gravar blocos de bits? Então tá, pra mim é um drive. E é assim que consigo montar coisas como um Google Drive ou Dropbox como se fosse um pendrive virtual também. Só que diferente de gravar os bits num arquivo local, o driver de sistema de arquivo do Google Drive vai mandar os bits pela rede pra sua conta no Google. Mas pro sistema operacional, foda-se. Ele mandou blocos, o driver de sistema de arquivos recebeu e disse que gravou. O sistema operacional tá pouco se lixando o que acontece com os bits, ele delega e só espera ouvir um &ldquo;ok, tá gravado&rdquo; e fica feliz.</p>
<p>O que fizemos com a ferramenta &ldquo;dd&rdquo; dá pra fazer o equivalente no Windows com a ferramenta Disk Management. Olha só, temos essa opção de &ldquo;Criar VHD&rdquo; que é virtual hard disk, que é o arquivão vazio. Vamos criar um simples de uns 10 megabytes. Uma vez criado posso anexar esse VHD e tornar ele disponível como um PhysicalDisk numerado. Agora ele aparece aqui embaixo, olha só. Ele vai se comportar como se fosse um pendrive ou HD externo. Eu posso escolher a opção pra formatar como NTFS e no final posso montar com um drive, digamos letra P. Agora o P: é um drive virtual e podemos mover arquivos, criar diretórios, o que quiser. E no final é só desanexar, que é o equivalente de ejetar.</p>
<p>De dentro do Linux posso usar aquele comando hexdump nesse arquivão VHD. E parecido com o arquivão hello.img que criamos com &ldquo;dd&rdquo;, é basicamente um linguição de zeros igualzinho. Olha a linha de zeros e o asterisco embaixo. Mas lá embaixo no arquivo, do endereço a00020 hexa em diante, tem um metadado pra identificar esse arquivão como um VHD. Só por causa desses bytes, que o arquivão que criei com &ldquo;dd&rdquo; não se enquadra como um VHD. Mas na prática é quase a mesma coisa: um linguição de bits zeros.</p>
<p>Voltando pro boot do Linux, parei no ponto onde o bootloader EFI o binário do kernel no diretório “/boot”. Esse arquivo normalmente é chamado de vmlinuz, muitas vezes o nome segue com a versão da kernel, como 5.15. A gente costuma chamar uma distro como Ubuntu ou Fedora de “Linux”, mas na verdade Linux mesmo é só esse um arquivo binário, a kernel. O resto do sistema operacional, o certo é chamar de GNU/Linux, ou seja, ferramentas GNU que rodam em cima de um kernel Linux. Quando o bootloader tem acesso a esse arquivo é quando iniciamos a segunda fase de boot.</p>
<p>Normalmente, no mesmo diretório &ldquo;/boot&rdquo; vai ter um outro arquivo, um &ldquo;initrd.img&rdquo;. Esses nomes vão variar de distro pra distro, mas no caso do Ubuntu e todo mundo que é derivado de Debian, o arquivo vmlinuz e o initrd.img são links simbólicos, tão vendo? O arquivo de verdade é esse que termina com &ldquo;generic&rdquo; no nome. A vantagem de ser link simbólico é porque posso querer deixar kernels mais antigas depois de um upgrade do sistema. Vai que a kernel mais nova veio com algum bug ou incompatibilidade e ferra meu sistema, impedindo bootar?</p>
<p>Não tem problema, se isso acontecer, basta resetar o PC. Vai voltar pro firmware UEFI da placa mãe que, por sua vez vai carregar o bootloader em &ldquo;/boot/efi&rdquo; ou o GRUB, que é aquela tela que tem no começo do boot da maioria das distros Linux. Eu sei que você só dá enter direto sem pensar muito em pra que ela serve. Nessa tela costuma ter uma opção pra justamente poder escolher outra kernel e outras opções de recuperação. Assim dá pra bootar na kernel da versão anterior, que sabemos que funcionava, e ter a oportunidade de consertar alguma coisa. Basta mudar o link simbólico pra kernel e initrd.img anterior e vai voltar a bootar normalmente até sair uma correção pra kernel mais nova. É raro acontecer, mas se acontecer existe essa opção.</p>
<p>Enfim, o arquivo initrd.img é um zipão comprimido, pode ser qualquer formato, a distro que decide. No caso do Ubuntu, eu falei “zipão”, mas não é exatamente um zip como feito pela ferramenta &ldquo;gzip&rdquo; mas sim um arquivo formato CPIO comprimido com LZ4, o Lempel-Ziv 4. Eu expliquei o que é Lempel-Ziv no video &ldquo;De 5 Tera a 25 Giga&rdquo;, depois assiste lá pra saber como compressão funciona. Enfim, o importante é que em Ubuntu podemos usar a ferramenta <code>unmkinitramfs</code> pra descomprimir essa imagem em qualquer lugar.</p>
<p>E olha só, se listarmos o que descomprimiu, vai parecer familiar com a estrutura de diretórios do seu Linux de verdade. Isso é o mínimo que a kernel precisa pra conseguir terminar o boot. Só que aqui descomprimimos essa imagem no nosso HD local. O grande lance é que depois do bootloader conseguir carregar a kernel, ela precisa ter acesso a esses arquivos pra conseguir carregar coisas como driver do seu HD. Por exemplo, e se seu HD tiver partição encriptada com LUKS? Ou se estiver em configuração de RAID dentro de um volume LVM? Como o kernel vai conseguir carregar o driver de RAID pra conseguir montar a partição se já não estiver compilada estaticamente?</p>
<p>São drivers como esse que estão nesse initrd. Só que o initrd em si precisa ser descomprimido em algum lugar. E aqui vou continuar simplificando, mas o que acontece é que a kernel pode não ter acesso ao seu HD no boot. Pode não ter os drivers pra isso. Mas ele tem acesso à RAM. Lembra o lance de criar um um arquivo com o comando &ldquo;dd&rdquo; vazio e formatar pra ser um pendrive virtual? Podemos fazer a mesma coisa na RAM, criar um espaço de bits que vai servir pra dar mount num sistema de arquivos e descomprimir o conteúdo do arquivo initrd pra lá.</p>
<p>Por isso que mesmo se você mandar encriptar sua partição principal ou configurar volumes em RAID, sempre vai ter pelo menos essa pequena partição de boot logo no começo do HD que é formatado em FAT32. Aí o firmware da máquina consegue acesso. Não desperdiça muito espaço, é super pequeno, com não muito mais que uns 100 megabytes. Por isso o initrd é um arquivo compactado, pra caber nessa partição. No Windows mesmo, se você abrir a ferramenta de Disk Management e olhar seu HD, veja, tem essa partição maior onde fica o Windows, e seria o que um BitLocker encriptaria se você escolher.</p>
<p>Mas olha aqui no começo, tem um partição pequena de EFI, que é o equivalente no Linux ao &ldquo;/boot&rdquo; que estamos analisando. Você não enxerga essa partição de boot EFI no Windows porque ele não monta com nenhuma letra de drive, pra não aparecer pros usuários e correr o risco deles estragarem o boot. No caso do Windows, lá no fim do HD tem outra partição escondida sem letra de drive que é a partição de recuperação. Ele é como se fosse o pendrive do Ubuntu que bootamos pra instalar. É o que boota quando você pede pro Windows iniciar em modo de recuperação pra conseguir diagnosticar algum problema que tá bloqueando o boot do Windows principal. É outro Windows, com kernel e drivers separados, em versão reduzida só com ferramentas de diagnóstico e recuperação.</p>
<p>No caso de Linux não temos uma partição assim, porque normalmente usamos pendrive mesmo. Mas é a mesma coisa, a gente gasta um espaço no HD pra ter a comodidade de não ter que sair procurando pendrive se uma hora seu Windows parar de bootar depois de um Windows Update. Já tá tudo no seu HD. Como espaço de HD é barato, podemos desperdiçar algumas centenas de megas. Se você tá pensando em apagar essa partição pra ter um pouco mais de espaço, é hora de pensar em comprar um HD maior, isso sim. Em Macs também tem uma partição escondida com um MacOS reduzido só com ferramentas de manutenção que você consegue acessar se na hora do boot deixar apertado Command + R.</p>
<p>Enfim. Nesse segundo estágio de boot, vamos acabar com um drive virtual em RAM, chamado initramfs ou file system de RAM de inicialização, onde o conteúdo do initrd.img vai ser descomprimido e com isso temos um mini Linux inicializado em memória. Agora sim, a kernel vai ter acesso a drivers extras que precisa e diversas outras ferramentas que pode precisar pra pular pra próxima fase.</p>
<p>Estamos na terceira e última fase do boot. Agora que começa aquela etapa que já viu no boot do seu Linux, quando vai correndo um monte de linhas que eu sei que você nunca parou pra ler. Aquele monte de linhas é o gerenciador de serviços reportando cada um dos serviços que tá conseguindo carregar naquele momento. Esse gerenciador pode ser o famigerado systemd que tem na grande maioria das distros modernas, ou o OpenRC que tem em distros como Slackware ou Gentoo, ou o Runit que acho que é um dos mais antigos, derivados da era dos UNIX originais. Acho que distros baseadas em BSD ainda usam também. Mas seja lá qual for, esse é o primeiro processo que a kernel vai carregar no seu sistema, o famoso processo com ID 1, o PID número 1.</p>
<p>Agora sim, esse sistema de inicialização vai se encarregar de carregar todo o resto dos serviços necessários pra ter um sistema usável. Agora ele consegue acessar a partição principal do seu HD e ler o arquivo &ldquo;/etc/fstab&rdquo;, que é a tabela de file systems onde tá declarado que partição é pra montar como &ldquo;/&rdquo;, a raíz do seu sistema operacional. Ele vai dar mount em tudo que precisa, incluindo os mounts especiais como o /proc, /dev, /sys. Vai carregar os serviços de USB pra achar seu mouse ou sua webcam, serviço de bluetooth que vai conectar com seu fone de ouvido, serviço de network que vai carregar seu wifi, serviço de dhcp client que vai pedir IP pro servidor de DHCP do seu roteador de internet e assim por diante.</p>
<p>Eu zoei que você nunca prestou atenção nesse monte de linhas subindo no boot porque vai rápido demais mesmo. Pra ver em detalhes o que aconteceu, use o comando <code>dmesg</code> depois do boot que lá vai ter essas linhas. Se um serviço deu pau, use o comando `systemctl statusad  e o nome do serviço pra ver se tem alguma dica. Esses serviços tem mais um detalhe na realidade. Logo que a kernel carrega um systemd ou runit, ele tem que ver em qual runlevel vai rodar. Isso também meio que varia, especialmente entre Linux e UNIX ou BSD, mas todos tem conjuntos diferentes de serviços que carregam em runlevels diferentes. Mas, Runlevel? Aí fodeu, deixa eu voltar na história um pouquinho.</p>
<p>Vamos voltar pro boot, a kernel tá carregada, ele carrega seu primeiro programa, de process ID 1, que antigamente era chamado simplemente de &ldquo;init&rdquo;. Esse init ia num diretório como &ldquo;/etc/rc3.d&rdquo; e lá ia achar vários links simbólicos pra scripts que costumavam ficar em &ldquo;/etc/init.d&rdquo;. Então cada sub-diretório &ldquo;rc1.d&rdquo; ou &ldquo;rc3.d&rdquo; podia ter coleções diferentes de links simbólicos de scripts.</p>
<p>Os scripts em si ficavam em &ldquo;/etc/init.d&rdquo;. É o que a gente chama de serviços ou daemons. Eram scripts de shell, que aceitavam parâmetros como &ldquo;start&rdquo; ou &ldquo;stop&rdquo; e dentro configuravam e executavam algum binário, por exemplo, programa e impressora pra montar o spool de impressão, ou o programa X que carrega a interface gráfica e assim por diante. Um daemon é um programa executável normal, com a diferença que tem esse script que inicia ou termina o programa. E o sistema de init executa esses scripts pra inicializar os serviços durante o boot do sistema.</p>
<p>Agora, nesse tal de &ldquo;/etc/rc3.d&rdquo; o “rc” é acrônimo pra &ldquo;run commands&rdquo; ou rode comandos do &ldquo;runlevel&rdquo; ou nível de execução 3. Parece complicado mas originalmente cada runlevel tem uma função específica, o runlevel 0 é pra procedimento de shutdown, o runlevel 6 pra reboot. Runlevel 1 é pra bootar em modo de um usuário só. Runlevel 3 é pra multi-usuário com rede. Runlevel 5 costuma ser pra bootar com interface gráfica.</p>
<p>Se você é de Windows, ele vem com pelo menos 2 runlevels, o do boot normal que cai no Windows e aquele &ldquo;Modo de Segurança&rdquo; ou &ldquo;Safe Mode&rdquo;, lembra? Que boota o Windows sem carregar todos os drivers e serviços, daí você fica sem rede, com resolução baixa, tudo pra quando tiver algum problema no boot, ter um ambiente pra bootar sem carregar quase nada, pra te dar a oportunidade de diagnosticar e consertar o problema. Runlevel é isso, quais conjuntos de serviços vai carregar ou não.</p>
<p>Vamos ver isso na prática. Abrindo o terminal de novo, podemos digitar o comando <code>runlevel</code> e vai dizer que estamos com o runlevel 5 carregado. Digamos que instalamos algum programa gráfico instável que travou tudo. Teclado não funciona, mouse não funciona, nenhuma janela se mexe. Vou simular carregando o Firefox. Faz de conta que foi ele que travou tudo. Nesse ponto a maioria de vocês ia apertar o botão de reset e rebootar. Mas a gente que é de Linux tem outra saída: usar a combinação de teclas Ctrl + Alt + F3 pra mudar pro runlevel 3 que é de multi-usuário com rede em modo texto.</p>
<p>Olha isso, saímos do modo gráfico, que continua rodando no runlevel 5. Agora fazemos login em modo texto e podemos usar comandos como <code>ps aux | grep firefox</code>. Lembra? Na nossa historinha, ele travou tudo, então vamos matar forçadamente com o comando <code>kill -9</code> e o PID dele. Pronto, agora podemos voltar pro modo gráfico. Normalmente eu diria que é só apertar a combinação Ctrl + Alt + F5 mas por alguma razão quando tava escrevendo o script do episódio, isso não funciona, mas Ctrl + Alt + F1 funciona. Enfim, veja como o programa de terminal continua aqui, mas o Firefox morreu. A resolução que zoou aqui, mas deve ser porque tá rodando numa máquina virtual. Esse é um dos jeitos de tentar destravar alguma coisa antes de ser drástico e dar reset na máquina toda.</p>
<p>Dá pra mudar de runlevels no terminal também usando o comando <code>telinit</code> com o número do runlevel. O runlevel 1 na realidade é usado pra manutenção e diagnósticos. E se você mudar pro runlevel 6, isso vai dar reboot na máquina, vamos ver? <code>sudo telinit 6</code> e olha só, rebootou. Sabe quando você usa no terminal comandos como <code>shutdown</code> ou <code>reboot</code>? É isso que ele tá fazendo, mudando pro runlevel 6. E por que isso é importante? Runlevels determinam quais conjuntos de serviços iniciar ou desligar.</p>
<p>Você nunca quer forçadamente apertar reset. Tem vários programas rodando em background, coletando arquivos de log, tem partições montadas que podem ainda não ter feito flush de caches. Você quer notificar todos os programas rodando pra fechar e limpar as coisas antes de realmente desligar a máquina, é o que chamamos de graceful shutdown ou desligamento gracioso. Se ficar dando reset forçado, uma hora vai corromper arquivos ou partições no sistema, por isso nunca force desligar, sempre dê halt, que é mudar pro runlevel 0. Vamos desligar? <code>sudo shutdown -h now</code> ou simplesmente <code>sudo telinit 0</code>.</p>
<p>Na prática, quando o sistema boots, a kernel carrega e inicia um processo 1 como o antigo &ldquo;init&rdquo; ou o atual &ldquo;systemd&rdquo;, ele vai carregar o conjunto de serviços declarados pro runlevel padrão, que costuma ser o runlevel 3 em servidores que não tem interface gráfica ou o runlevel 5 em desktops e PCs como de vocês assistindo, que vai bootar os serviços pro modo gráfico. Na prática significa que você tem diversas formas diferentes de bootar, só que o padrão pra maioria das instalações é entrar no runlevel 5 que é gráfico.</p>
<p>Vamos fazer outro exercício. Digamos que de jeito nenhum consigo entrar no modo gráfico depois de um upgrade ou script mal feito que rodei na máquina, sei lá. Não boota. Nesse ponto a maioria das pessoas já ia pensar &ldquo;que bosta, cadê meu pendrive, vou reinstalar tudo do zero&rdquo;. Calma! No Ubuntu, ele faz como o Windows, tenta ir direto pra tela gráfica. Mas se antes do computador ligar deixarmos a tecla shift apertada, a tela do GRUB vai aparecer.</p>
<p>GRUB é acrônimo pra Grand Unified Bootloader. Como o nome diz, é um bootloader. É ele que o computador vai carregar no boot, e depois se encarregar de achar a kernel do Linux e passar o controle pra ele. O Ubuntu sempre boota com Grub, mas ele carrega quieto, por isso que nunca vemos. Com a tecla shift apertada forçamos a aparecer. Por padrão vai bootar com essa primeira opção aqui, mas podemos apertar a tecla &ldquo;e&rdquo; pra editar os comandos de boot da kernel. Daí vamos pra essa linha aqui embaixo e no final escrevemos o número 3, indicando que queremos bootar no runlevel 3 em vez do padrão que é o 5. Com ctrl + x saímos e vai bootar os serviços do runlevel 3, e olha só, não carrega mais o modo gráfico, podemos logar em modo texto e tentar consertar seja lá o que eu fiz de errado. Daí, no próximo boot vai normalmente pro runlevel padrão que é o 5, de modo gráfico.</p>
<p>Então a sequência é sempre essa: computador liga, carrega o firmware UEFI da placa mãe que vai inicializar o hardware e os dispositivos. Ele vai procurar a primeira partição no HD ou pendrive que tenha o diretório /boot/efi. No nosso caso, dali vai carregar o bootloader do GRUB. O GRUB por sua vez tem uma configuração, que foi o que editamos agora, que diz onde tá a kernel e onde tá a imagem do initrd. Executa a kernel, monta uma partição temporária na RAM chamada initramfs e descomprime a imagem do initrd lá.</p>
<p>Finalmente, a kernel vai carregar um gerenciador de serviços como o systemd como processo número 1 que, por sua vez, vai começar a carregar o resto dos serviços, conforme declarado na runlevel padrão. Cada serviço tem declarado quais outros serviços ele precisa esperar carregar, qual dependência tem, e é isso que vai aparecendo naquele monte de linhas que vai subindo na sua tela no boot. Até finalmente terminar de carregar o servidor Xorg, e o gerenciador de login, no nosso caso, o GDM do GNOME e é aí que aparece a tela gráfica de login.</p>
<p>Esses conceitos de runlevels, diretórios de scripts de serviços em &ldquo;/etc/init.d&rdquo;, links simbólicos pros scripts em diretórios como &ldquo;/etc/rc5.d&rdquo; e o processo chamado &ldquo;init&rdquo; que era o antigo gerenciador de serviços, é o sistema que chamamos System V ou SysV que nasceu nos UNIX originais. Foi assim que eu aprendi em UNIX dos anos 90, e é assim que gerenciadores como o Runit e acho que o OpenRC ainda funcionam. Mas em distros modernas, nada disso vale mais, pode esquecer tudo que eu falei.</p>
<p>Distros como Ubuntu, Fedora, OpenSuse, Manjaro e muitos outros, especialmente se usam gerenciador de janelas GNOME, aderiram ao SystemD e ele descarta todos esses conceitos. Não existe runlevels em systemd e sim targets. E apesar de ter diretórios como &ldquo;rc3&rdquo; ou &ldquo;rc5&rdquo; eles não são mais usados. Em vez disso existem diretórios como &ldquo;graphical.target&rdquo; ou &ldquo;multi-user.target&rdquo;. E eles tão espalhados em diversos diretórios como &ldquo;/etc/systemd/system&rdquo; ou &ldquo;/usr/lib/systemd/system&rdquo; e assim vai. Ele tenta simular o comportamento dos runlevels pra manter compatibilidade com comandos como <code>telinit</code> que usamos antes.</p>
<p>É uma das razões de porque tanta gente odeia o systemd e você vai achar fios de reddit e grupos em discord de gente xingando. Eu mesmo não entendo o systemd a fundo, nem de perto. Ele faz bem mais que o Init de SysV antigo e tenta fazer muito mais coisas por baixo dos panos. Eu me acostumei a usar, mas quem desenvolve pra Linux deve ter mais motivos técnicos de porque isso incomoda. Só o fato de mudar conceitos tradicionais com runlevels pra targets, deve dar um trabalho extra pra manter software pra diferentes distros que não aderiram a esse novo padrão.</p>
<p>Por exemplo, em &ldquo;/usr/lib/systemd/system&rdquo; tem esses links simbólicos que tenta equiparar o que seriam os antigos runlevel numerados com os atuais targets. Veja, o runlevel5 é um link simbólico pra graphical.target. O runlevel6 é link pro reboot.target. Vamos ver o que é o graphical.target e olha só, é uma configuração que fala que requer carregar a mesma coisa que o multi-user.target, o antigo runlevel 3, mas também quer carregar o display-manager.service. Faz sentido, o modo gráfico nada mais é do que o modo multi-usuário que carrega em texto, mas carregando o serviço de tela pra mostrar interface gráfica.</p>
<p>No terminal, com o comando &ldquo;runlevel&rdquo; ainda consigo ver os runlevels rodando agora, e com o novo comando <code>systemctl get-default</code> consigo ver que o target padrão de boot é o graphical.target. Se quiser mudar pra das próximas vezes bootar em modo texto, posso usar o comando <code>systemctl isolate multi-user.target</code>, que é mais ou menos o equivalente antigo a usar o comando &ldquo;telinit&rdquo;. Cuidado ao fazer isso porque ele vai rebootar o sistema, mas não vai mudar o target padrão, que ainda vai ser graphical.target.</p>
<p>Pra todas as vezes bootar só em modo texto, posso mudar o target padrão fazendo <code>systemctl set-default multi-user.target</code>. Esse comando &ldquo;systemctl&rdquo; você vai ver em diversos tutoriais. Quando instala Docker por exemplo, o tutorial costuma mandar habilitar o serviço pra carregar em todo boot fazendo <code>systemctl enable docker</code>, que vai adicionar no target de graphical.target. E pra carregar o serviço na hora pode fazer <code>systemctl start docker</code>. Pra ver se o serviço tá carregado, basta fazer <code>systemctl status docker</code>, e pra ver todos os serviços que estão carregados agora é só rodar <code>systemctl --type service</code>.</p>
<p>A maioria dos tutoriais hoje assume que você está usando uma distro com systemd e por isso sempre vai pedir pra rodar comandos como <code>systemctl start</code>. O systemd em si faz bem mais coisas que iniciar ou rebootar serviços, então recomendo que leia sobre ele em wikis como do site do Arch ou Gentoo, que costumam ter exemplos de como usar e mais detalhes do funcionamento. Como disse antes, eu mesmo não sei todos os detalhes dele.</p>
<p>Enfim, o objetivo de hoje foi juntar o antigo video de Ubuntu e um pouco dos videos de WSL2 com a minissérie sobre armazenamento. Recomendo que assistam agora se ainda não viram. Pra continuar a minissérie, no próximo episódio quero explicar mais conceitos de Linux mostrando como uma instalação realmente funciona, então se ficaram com dúvidas, mandem nos comentários abaixo, se curtiram o video deixem um joinha, assinem o canal pra não perder o próximo episódio e compartilhe o video com seus amigos. A gente se vê, até mais!</p>
]]></content:encoded><category>linux</category><category>tutoriais</category><category>ciencia-da-computacao</category><category>akitando</category></item><item><title>[Akitando] #80 - O Guia +Hardcore de Introdução à COMPUTAÇÃO</title><link>https://akitaonrails.github.io/2020/06/04/akitando-80-o-guia-hardcore-de-introducao-a-computacao/</link><guid isPermaLink="true">https://akitaonrails.github.io/2020/06/04/akitando-80-o-guia-hardcore-de-introducao-a-computacao/</guid><pubDate>Thu, 04 Jun 2020 09:44:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/8G80nuEyDN4"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2&gt;DESCRIÇÃO&lt;span class="hx:absolute hx:-mt-20" id="descrição"&gt;&lt;/span&gt;
&lt;a href="#descri%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Errata:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;em 53:46 eu falo &amp;ldquo;1 1&amp;rdquo; mas era &amp;ldquo;aa&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Se tem um tema que eu queria fazer desde que abri o canal é apresentar algumas das fundações da computação, tanto do ponto de vista de história e da evolução, mas principalmente dos fundamentos básicos que eram vários 60 anos atrás e ainda são válidos até hoje.&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/8G80nuEyDN4"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h2>DESCRIÇÃO<span class="hx:absolute hx:-mt-20" id="descrição"></span>
    <a href="#descri%c3%a7%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Errata:</p>
<ul>
<li>em 53:46 eu falo &ldquo;1 1&rdquo; mas era &ldquo;aa&rdquo;</li>
</ul>
<p>Se tem um tema que eu queria fazer desde que abri o canal é apresentar algumas das fundações da computação, tanto do ponto de vista de história e da evolução, mas principalmente dos fundamentos básicos que eram vários 60 anos atrás e ainda são válidos até hoje.</p>
<p>O video de hoje vai abrir com um tema de videogames, mas vamos descer bem fundo no cérebro do Nintendinho pra criar um vocabulário que eu vou usar na Parte 2, quando de fato vou mexer num emulador de Nintendo. E esse vocabulário vai servir não só pra videos futuros meus como pra tudo que você for fazer em computação. Esta é a base, sem esta base tudo que você tentar aprender de avançado depois vai ser mais difícil.</p>
<p>Infelizmente é muito difícil empacotar estes temas de uma forma que não seja maçante. Espero ter conseguido pelo menos interessar vocês no assunto.</p>
<p>Canais:</p>
<ul>
<li>Ben Eater: <a href="https://www.youtube.com/user/eaterbc"target="_blank" rel="noopener">https://www.youtube.com/user/eaterbc</a></li>
<li>Retro Game Mechanics Explained: <a href="https://www.youtube.com/channel/UCwRq"target="_blank" rel="noopener">https://www.youtube.com/channel/UCwRq</a>&hellip;</li>
</ul>
<p>Playlist:</p>
<ul>
<li>Build a 65c02-based computer from scratch: <a href="https://www.youtube.com/watch?v=LnzuM"target="_blank" rel="noopener">https://www.youtube.com/watch?v=LnzuM</a>&hellip;</li>
<li>Building an 8-bit breadboard computer!: <a href="https://www.youtube.com/watch?v=Hyznr"target="_blank" rel="noopener">https://www.youtube.com/watch?v=Hyznr</a>&hellip;</li>
</ul>
<p>Links:</p>
<ul>
<li>Ben Eater “Build a 6502 Computer” Kit: <a href="https://eater.net/6502"target="_blank" rel="noopener">https://eater.net/6502</a></li>
<li>VASM A portable and retargetable assembler: <a href="http://sun.hasenbraten.de/vasm/"target="_blank" rel="noopener">http://sun.hasenbraten.de/vasm/</a></li>
</ul>
<h2>SCRIPT<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Se você está cético sobre este episódio porque não tem nenhum interesse por videogames, não se preocupe, a intenção de hoje é apresentar muitos dos conceitos fundamentais pra você entender melhor o que é programação. Este vai ser mais um daqueles episódios que você vai ficar se perguntando &ldquo;mas onde diabos esse japonês quer chegar&rdquo; mas com paciência as peças se encaixam. E já aviso que esse também vai ser um vídeo bem complexo. Pausa, volta e assiste de novo. Não é um conteúdo que dá pra entender 100% assistindo só uma vez.</p>
<p>Com tudo isso fora do caminho vamos lá. Quem me acompanha nos Instagram da vida sabe que um dos meus hobbies favoritos é jogar videogames. Além dos jogos Triple A eu também gosto bastante de voltar aos jogos da minha infância, de Alex Kidd a Sonic a Super Mario. Eu não faço coleção de cartuchos porque não teria espaço físico pra tudo. São milhares e milhares de jogos. Felizmente hoje é muito simples. Basta baixar uma das dezenas de emuladores e carregar a ROM dos games. Por exemplo, posso carregar este Retro Arch que tenho no meu desktop e boom, Super Mario.</p>
<p>A idéia deste video veio quando eu tava assistindo dois dos meus canais de YouTube favoritos. O primeiro é o Retro Game Mechanics Explained. O cara é muito bom. Por exemplo, pra quem gosta do Pokemon original de GameBoy, ele explica bit a bit o mistério do elusivo MISSINGNO, recomendo que assistam. Mas o video que eu tava assistindo me relembrou de outro dispositivo que existia durante os anos 90.</p>
<p>Muitos de nós gostamos de games só que mais como passatempo, e não somos tão obsessivos em completar 100% dos jogos ou fazer speedruns pra bater o recorde de quem acaba no menor tempo, e só porque um jogo é antigo não quer dizer que é fácil. Muitos tinham dificuldade maior e mais brutal que jogos da atualidade. Tentem jogar coisas como o Megaman original ou Battletoads do Nintendinho pra ver o que quero dizer. Fora que antigamente nem todo jogo tinha uma bateria pra salvar o progresso, então se dava game over você realmente tinha que recomeçar do zero.</p>
<p>Então surgiram dispositivos que possibilitavam &ldquo;cheats&rdquo;. Quem joga online conhece essa palavra e os diversos software anti-cheat pra evitar que jogadores trapaceiem com hacks. Anti-cheats que vira e mexe criam polêmica, como no recente caso do jogo Valorant, cujo software anti-cheat foi acusado de causar até estragos de hardware.</p>
<p>Enfim, nos anos 90 a tecnologia era mais simples. Existiam diversas marcas diferentes como Action Replay, Game Shark ou o mais famoso, o Game Genie. Funcionava assim, você plugava o cartucho na parte de cima e daí plugava o conjunto no console. O Game Genie ficava entre o cartucho e o console. Ele engana o console. Por exemplo, o console pergunta pro cartucho, com quantas vidas que o Super Mario tem que começar? Daí tá gravado em algum lugar no cartucho o número 3, mas aí se você colocar um código mágico no Game Genie, como AATOZE, ele devolve 9 pro console no lugar de 3 e o jogo começa com 9 vidas.</p>
<p>Eu vou explicar o game genie em mais detalhes na Parte 2 deste video, de uma forma complementar que o Retro Game Mechanics explica. Mas antes de chegar nisso vou fazer uma longa tangente hoje com meu segundo canal favorito, o Ben Eater. O canal do Ben é hardcore, ele explica todas as fundações eletrônicas da computação, em particular exatamente a arquitetura por trás de computadores como o nintendinho. Eu vou usar muito material do canal dele e condensar num único video pra tentar fazer vocês finalmente enxergarem a mulher de vermelho no código do Matrix.</p>
<p>(intro)</p>
<p>O nintendinho roda o famoso processador MOS 6502 de 8-bits, derivado do desenho do Motorola 6800 e fabricado pela MOS Technologies a partir de 1975. Esse processador foi usado não só no nintendinho mas em dezenas de outros consoles e computadores pessoais dos anos 80 incluindo coisas como o lendário Apple II, o avô dos Macintosh, ou o concorrente Atari, e os britânicos Commodore e BBC Micro. Ele foi um dos componentes que ajudou a iniciar a revolução dos computadores pessoais dos anos 80. Exatamente por isso é um bom ponto de partida pra entender a computação de hoje em dia.</p>
<p>Apesar de internamente processar em 8-bits, ele possui uma interface de comunicação de 16-bits. 16-bits em hexadecimal, que são 2 bytes, se representa com 4 caracteres. Já um valor de 8-bits, ou 1 byte, se representa com dois caracteres. Como o código mágico AATOZE que falei tem 6 caracteres então já começa a ficar fácil de entender o que está acontecendo aqui. Muito provavelmente é um endereço de 16-bits e um valor de 8-bits, exatamente o que o 6502 entende. E de fato esse código se transforma no endereço 906A e no valor 09, que dá as 9 vidas ao Super Mario.</p>
<p>É importante que vocês entendam como enxergar binários e hexadecimais. Se você já sabe disso, pode pular pra este tempo aqui em baixo. De qualquer forma, tudo em computação é binário. Este vídeo que você tá assistindo é uma stream de bits. O jogo do Super Mario é um punhado de bits. Um arquivo de texto ou código em Javascript é um arquivo de bits. Muita gente acha que arquivos texto e binário são diferentes. Mas não, ambos são binários. A diferença é como você representa esse binário depois de abrir, pra mostrar pro usuário.</p>
<p>Deixa eu mostrar. Na maioria dos sistemas operacionais existem editores de texto, seja o miserável Notepad no Windows ou o moderno VS Code. Se eu abrir um arquivo que contém texto, qualquer um vai abrir sem nenhum problema. Mas se no mesmo editor eu tentar abrir o arquivo da ROM do Super Mario, vai aparecer esse monte de lixo na tela. Isso porque um editor de textos só sabe interpretar um sub-conjunto de binários que, por convensão, representa letras, números e símbolos.</p>
<p>Se eu abrir um editor e escrever a palavra &ldquo;Hello World&rdquo; e gravar, podemos abrir o binário cru com um visualizador de binário, como o comando <code>xxd -b</code> e pronto, isto é o que tá gravado no arquivo. Veja este primeiro bloco de 8 bits é a letra &ldquo;H&rdquo;, vejam estes dois blocos repetidos, são os dois &ldquo;L&rdquo; e assim por diante. Claro que ler bit a bit assim é bem inconveniente, então podemos pedir pra ferramenta converter esses bits em uma representação hexadecimal, rodando o mesmo comando xxd sem o parâmetro -b temos os seguite.</p>
<p>Agora sim, aquele bloco de bits é o número 48 em hexadecimal, que não é a mesma coisa que 48 em decimal. Já vou explicar. Essa é a letra &ldquo;H&rdquo;. Depois temos 6c e 6c que obviamente é o código hexadecimal pra letra &ldquo;L&rdquo;. E assim por diante. Quem disse que 48 é H ou que 6c é L? É uma convenção internacional conhecida como Unicode. É um conjunto de tabelas e regras que mapeiam cada caracter em todas as línguas num código de 1 ou mais bytes. Emojis também, cada um tem seu código. Tudo é número. A tabela mais antiga e mais simples que ainda acabamos usando é a ASCII.</p>
<p>Pra quem não sabe, o que chamamos de &ldquo;ROM&rdquo; em retro gaming são os bits que vem gravados num cartucho e que extraimos pra dentro de um arquivo, é isso que um emulador de nintendinho lê no lugar do cartucho de verdade. Com o mesmo comando <code>xxd</code> mas passando com um pipe pro <code>more</code> pra podermos ver as primeiras linhas, temos o seguinte. Deixa eu explicar melhor o que é essa representação do comando xxd. Aliás, existem diversas ferramentas que conseguem abrir binários, um dos mais usados é o <code>hexdump</code> mas prefiro mostrar o xxd pra dar outra alternativa. A diferença dos dois é que o hexdump só mostra o binário na forma de hexadecimal, o xxd faz a mesma coisa mas consegue também gravar de volta pro binário. Por isso se somar o xxd com o vim você consegue editar qualquer binário diretamente.</p>
<p>Bom, a coluna da direita são os endereços, seguido de uma linha de 16-bytes organizados em grupos de 4 letras, cada um com 2 bytes, num total de 8 blocos de 4 letras. Cada 1 letra é um byte em hexadecimal. Você começa no endereço 0 e vai contando até 15 que é a letra F, dai na segunda linha o endereço começa em 16 que é o hexadecimal 10. Conta mais 16 elementos, agora chega no elemento 32 que em hexa é 20 e assim por diante.</p>
<p>Na 3a coluna ele mostra &ldquo;.&rdquo; (ponto) quando o byte não tem representação em texto. Quando tem, ele mostra o que seria o código na tabela ASCII. Vocês vão notar que nos primeiros 3 bytes temos aparecendo o texto &ldquo;NES&rdquo; nessa terceira coluna. NES é como o Nintendinho é chamado nos Estados Unidos, o Nintendo Entertainment System. Obviamente não é coincidência, todo cartucho distribuídos nos Estados Unidos começa com esse código.</p>
<p>Isso é só uma etiqueta arbitrária pra facilitar identificar o que é esse binário. Muitos outros formatos de arquivo fazem a mesma coisa. Se eu abrir uma imagem formato PNG com o xxd olha só, aparece um código 89 e depois os códigos ASCII que forma a palavra PNG. Se abrirmos uma imagem JPEG todos começam com FF D8 FF E0 e alguns bytes pra direita aparece JFIF que é sigla pra JPEG File Interchange Format. Se listar um binário compilado com extensão .class de Java olha o que aparece. O identificador de todo binário Java é o hexadecimal que forma as palavras &ldquo;cafe babe&rdquo;.</p>
<p>Se não ficou claro, o que estamos vendo na tela com o xxd não é o binário de verdade, assim como um editor de textos como Notepad mostra uma representação em forma de texto legível dos números binários reais, o xxd mostra os binários de uma forma que seja fácil da gente conseguir acompanhar. Essas colunas da esquerda e direita não existem. E não existe quebra de linha. Um binário são os bits escritos um atrás do outro numa única linha contínua até o fim. Pense num arquivo binário como um rolo de fita. Entendem agora porque você vê computadores antigos gravando dados num rolo de fita? Ou num cassette? É assim mesmo que funciona até hoje. Se você já programa em alguma coisa, pense num binariozão com um Array.</p>
<p>Existem vários sites e videos que ensinam em detalhes como converte e faz contas em binário e hexadecimal então vou resumir hiper resumido. Basicamente sistema decimal representamos números que vão de 0 a 9. Em hexadecimal representamos números indo de 0 a F, onde A é 10, B é 11, até F que é 15. Todas as regras aritméticas de soma, subtração etc funcionam igual. Quando você faz F + 1, coloca zero e sobe 1, daí o hexadecimal 1 0 equivale a 16. Sim, você precisa se acostumar a manipular números em hexadecimal. Até mesmo quando trabalha com front-end e CSS sabe que as cores são mapeadas em números hexadecimais e você faz conta com elas pra fazer degradê de cores por exemplo, você subtrai pra tornar todas as cores mais escuras ou faz soma pra elas ficarem mais claras.</p>
<p>Voltando pro cabeçalho do Java, o primeiro &ldquo;C&rdquo; não é a letra do alfabeto &ldquo;C&rdquo; e sim o número 12 em hexadecimal. Já brincaram com calculadora de cabeça pra baixo? Por exemplo, você digita 07734 e vira a calculadora e temos &ldquo;HELLO&rdquo; ou digitamos 376006 e de cabeça pra baixo temos Google. Todo mundo já brincou disso e o CAFE BABE do java é parecido só que como em hexadecimal temos de verdade as 6 primeiras letras do alfabeto, dá pra representar palavras. Mas é só uma brincadeira mesmo.</p>
<p>Agora, voltando ao binário. Computar em bits tem algumas vantagens pro computador fazer alguns tipos de conta. Em particular multiplicar por 2 e dividir por 2. Pegue o número 4 que em 4-bits seria 0 1 0 0. Se eu mover 1 pra esquerda vira 1 0 0 0 que é 8. E pra dividir? Oposto, só mover tudo pra direita então o 4 que é 0 1 0 0 vira 0 0 1 0 que é 2. Estou simplificando, claro, porque se dividir um número ímpar, eu não vou ter o resto assim. Aliás, isso que eu falei de mover pra esquerda ou direita é o que se chama de operador bitwise em qualquer linguagem moderna, o shift left que é &laquo; e shift right que é &raquo;.</p>
<p>Se abrir qualquer navegador e inspecionar um elemento, você abre o tal console de Javascript. E aqui podemos exercitar o que eu acabei de falar. Por exemplo, se eu quiser representar um número em binário começo digitando 0b daí por exemplo 0 1 0 0 que é 4. E é exatamente isso que ele mostra. Vou criar um texto chamado &ldquo;palavra&rdquo; e dentro colocar &ldquo;hello world&rdquo; que tem 11 letras. Se eu quiser pegar a décima primeira letra dessa palavra posso fazer <code>palavra[10]</code> e 10 porque em programação tudo começa de 0. Então 0 é &lsquo;h&rsquo;, 1 é &rsquo;e&rsquo; etc e a posição 10 é a letra &ldquo;d&rdquo;.</p>
<p>Podemos chegar na mesma letra se fizermos <code>palavra[0x0A]</code> lembram que eu falei que A em hexa é 10? Pro Javascript saber que isso na verdade é um número colocamos 0 x antes. Pra ficar mais hardcore podemos fazer <code>palavra[0b1010]</code> e esse 0 b é pro Javascript saber que é um número binário. Tanto o 0xA quanto 0b1010 são formas diferentes de escrever o mesmo número 10. É tudo o mesmo número, só que podemos representar de formas diferentes. Internamente sempre é tudo um número binário. As linguagens de programação só facilitam e nos deixam usar números em decimal ou mesmo letras.</p>
<p>Se eu fizer 10 shift right 1 é divisão por 2, e eu posso fazer <code>palavra[0xA &gt;&gt; 1]</code> onde A é 10 em hexa, shift right 1 que é divisão por 2 e ele devolve espaço que é o que tem na posição 5 naquele hello world.</p>
<p>Entendido isso, vamos pular pra outro conceito. Todo mundo ouve falar sobre computadores 32 bits ou 64 bits. Se você pensar em base decimal dá impressão que estamos só dobrando de 32 pra 64. O que isso significa? Em resumo significa que internamente o computador consegue fazer cálculos e processar números de 32 ou 64 bits de comprimento de uma só vez. Até agora eu fiz exemplos com números de 4 bits que dá pra contar só até 255. Lembra que eu falei que cada 1 bit que eu adiciono pra esquerda eu estou multiplicando o número todo por 2? Então 5 bits já é o dobro de 4 bits. 6 bits é o dobro de 5 bits. 33 é o dobro de 32 bits e 64 é 32 bits ao quadrado. A metade de 64-bits não é 32 e sim 63-bits.</p>
<p>Em 32 bits podemos representar até quase 4.3 bilhões. Em 64-bits podemos representar um número gigante 1.8 x 10 elevado a 19, ou seja, um numerozão de 19 casas. É a casa dos quintilhões. Entendam essas relações de dimensão. A vantagem de usar hexadecimal é que esse numerozão de 64-bits pode ser representado por 16 caracteres. Mesmo que você ainda esteja coçando a cabeça neste ponto, veja que não é coincidência que o numerozão de quintilhões de 64-bits fica bonitinho tudo com FFFFF&hellip; em hexa. Isso porque propositalmente usamos números derivados da base 2. Por isso um megabyte não é 1000 bytes e sim 1024 bytes. Em hexa seria 4 0 0. Tendemos sempre a usar números que são redondos em hexadecimal.</p>
<p>De qualquer forma 64-bits é um número difícil de visualizar na cabeça. Não se preocupe, eu acho que até o final do video vocês vão começar a se acostumar com isso. Mas eu acho que a forma mais fácil de entender como as coisas funcionam hoje é ver como funcionava ontem, porque o que temos hoje são mais ou menos camadas extras em cima da mesma base de antigamente. Componentes mais modernos, métodos de fabricação melhores, mas a fundação ainda é exatamente a mesma. Por isso quero voltar pros anos 70, mais especificamente pra 1975 e olhar mais de perto o cérebro do nosso nintendinho.</p>
<p>Se abrirmos o nintendinho, esta é a placa mãe com os diferentes chips. Mas por agora vamos nos focar neste aqui, o MOS 6502 que eu falei antes. Se eu quisesse mostrar o nível zero de verdade da fundação, precisaria começar com o básico em eletrônica. Mas eu também só sei o básico. É o que você aprenderia em Engenharia Elétrica ou Engenharia da Computação e não em Ciências. Mas eu quero resumir pelo menos, e pra isso resolvi pegar emprestado material de outro dos meus canais favoritos, do Ben Eater, que eu falei no começo do video.</p>
<p>Uma das playlists dele mostra como você constrói um processador de 8-bits do zero. E outra playlist dele, por acaso é como fazer um hello world com o 6502, o mesmo CPU do nintendinho. Só pra mostrar um Hello World ele levou meses produzindo uns 9 videos com mais de mais hora cada. São horas de video e eu vou resumir tudo em alguns minutos, então depois eu recomendo assistirem os videos dele com calma. Como eu disse daria pra descer no nível da física elétrica mas eu acho que a partir do nível que eu vou mostrar neste video é o mínimo suficiente pra se ter um desenho na cabeça pra conseguir visualizar o que um programa de fato está fazendo no hardware. Eu acho muito mais difícil aprender a programar sem entender o que acontece por baixo.</p>
<p>Quem está iniciando em eletrônica hoje em dia tem bastante material da hora pra treinar. Uma dessas ferramentas se chama breadboard, que é uma placa feita pra montar protótipos de circuitos eletrônicos sem precisar ficar soldando os componentes, você só encaixa os chips e fios na placa, liga a força, normalmente de 5V e pronto. Fazer circuitos integrados é literalmente programação de baixo nível. Ele inclusive vende kits com todos os componentes que você vai ver. Se tiver como investir, recomendo como bom aprendizado. É como se fosse brincar de Lego, mas bem mais interessante.</p>
<p>A lógica por trás de linguagens de programação modernas seguem os mesmos princípios da lógica em nível de hardware. Se você já ouviu falar isso de &ldquo;ter que saber lógica pra programar&rdquo; mas nunca entendeu onde começar, comece entendendo portas lógicas e máquinas de estado.</p>
<p><a href="https://www.youtube.com/watch?v=LnzuMJLZRdU"target="_blank" rel="noopener">https://www.youtube.com/watch?v=LnzuMJLZRdU</a> (part 1)</p>
<p>Pra começar veja o diagrama do 6502. Todo chip tem essas perninhas ou pinos, nelas, você liga ou desliga corrente em alguns pinos pra se comunicar com o chip, e os traços recebem corrente quando o chip quer enviar dados. Numa placa de verdade com solda e tudo, o que liga um pino de um chip a outro são traços condutores. Num breadboard se usa fios de metal mesmo pra facilitar. Agora vamos ver o diagrama de pinos, e a primeira coisa que quero chamar a atenção são esses pinos que vão de A0 até A15 e os pinos que vão de D0 até D7. Não é coincidência que são 16 pinos A e 8 pinos D.</p>
<p>Eu disse que o 6502 é um processador de 8-bits então quando você quer ler o resultado de algum processamento que ele fez, serão valores de 8 bits de comprimento, e eles vão trafegar por esses pinos D0 até D7. E quando o processador quer enviar um endereço pra buscar na memória RAM por exemplo, ele pode mandar um número de até 16-bits, que trafega por esses pinos de A0 até A15. Digamos que eu quero mandar o número em hexa <code>a9</code> pelos pinos de dados. Em binário seria 1 0 1 0 1 0 0 1, e cada um desses bits vai em um pino de D9 a D0.</p>
<p>Cada componente que coloca no Breadboard sempre vai ligar um pino de força e outro pino em ground ou Terra, é como você passa energia pro componente. Daí ele vai ligar outros pinos, por exemplo pra habilitar a CPU, outro pra conectar no clock. Um clock é exatamente o que o nome diz, é um componente com um cristal de quartzo que oscila, enviando um sinal elétrico numa frequência precisa e constante. Todo componente que processa alguma coisa no computador precisa estar ligado no mesmo clock pra tudo funcionar sincronizado. Um computador é uma grande orquestra de componentes e o clock é o maestro. Muitas instruções do CPU demoram mais de um clock, mas tudo é alinhado de acordo com a frequência desse clock. A menor unidade de processamento num computador é esse pulso que eu vou ficar chamando de clock durante o video.</p>
<p>Um clock de sistema convencional é pequeno como esse de 1 megahertz, ou 1 milhão de pulsos por segundo, que o Ben está mostrando, mas ele vai usar um mais feinho que ele mesmo construiu em outro video. A vantagem é operar mais devagar e dar a opção de poder pausar o clock e manualmente soltar um pulso de cada vez. todo componente do computador, incluindo a CPU, vai pausar junto e rodar um ciclo de clock de cada vez. Um processador 6502 original não suportava uma pausa dessas, mas esse que ele está usando é uma versão mais moderna que é fabricado até hoje e tem mais funcionalidades.</p>
<p>Continuando ele precisa ligar o pino que indica pro CPU rodar que é o RDY ou ready, depois o pino que liga com um botão de reset que é o RESB, daí liga o pino de clock e ligamos a placa do clock com força e terra na placa principal.</p>
<p>Só de fazer isso e ligando a placa principal numa tomada de 5V a CPU já tá funcionando e fazendo alguma coisa. Uma forma simples de monitorar pinos de dados é ligar LEDs direto em cada um deles, por exemplo ligando os pinos A0 a A4 em 5 LEDs pra gente ver se tem alguma coisa acontecendo. E de fato tem alguma coisa, mas bem aleatório.</p>
<p>Pra monitorar melhor, o Ben montou um sistema de monitoramento com Arduino em outro video e vai usar aqui. Um Arduino é um microcontrolador que tem algumas dezenas de pinos que servem tanto como entrada de dados ou saída. Então ele vai ligar primeiro os pinos de A0 a 15 da CPU com os pinos 22 a 52 no Arduino. O Arduino é programável, e podemos escrever programinhas simples e enviar pra ele executar. E é isso que o Ben vai fazer. Não se preocupe se você nunca viu um programa de Arduino, o código dessa demonstração é hiper simples, só prestar atenção que dá pra entender sem nenhum problema.</p>
<p>Primeiro vamos declarar uma lista com quais pinos do Arduino vamos trabalhar, e vai ser de 22 a 52.
Daí fazemos um loop lendo número a número e mandando um comando pra dizer que vamos ouvir o que vier nesse pino, ou seja, eles são INPUT
Agora fazemos outro loop pela mesma lista pra ir lendo o que vem em cada pino. Essa função devolve true ou false então quando vier true devolvemos o número 1 e quando vier false devolvemos 0 e escrevemos na tela
Daí escrevemos uma quebra de linha com print line e repete tudo de novo.
Finalmente não podemos esquecer de declarar a velocidade que vamos trafegar dados que vai se 57600 bauds, não se preocupe com esse número mágico.</p>
<p>Transmitimos o programa pro Arduino via essa interface, e abrimos o Monitorador de Sistema pra ver o resultado desse programa rodando. E vemos um monte de números binários, 16 bits no total que é o número de pinos que estamos monitorando. Entenda o que está acontecendo, o CPU está processando alguma coisa e setando bits nos pinos de endereço. Esses pinos estão ligados no Arduino que está lendo esses bits. Daí estamos num loop infinito puxando esses bits constantemente e imprimindo na tela. Mas não estamos ligados ao clock então ele vai ficar que nem doido lendo e imprimindo sem parar, sem qualquer ordem. Pra melhorar podemos sincronizar o Arduino ao mesmo clock também.</p>
<p>Eis um exemplo de porque os componentes precisam estar sincronizados com o clock. O Arduino não sabe quando ele precisa ler dos pinos, então fica lendo que nem doido e não faz sentido nenhum. Ligando ao clock, ele vai ler só uma vez a cada pulso. Entao em vez de fazer um loop infinito vamos ligar o código de leitura ao evento de um pulso do clock. Assim, se pausarmos o clock o programa também vai pausar, porque nenhum evento vai ativar o código de leitura.</p>
<p>Podemos ligar o clock em qualquer lugar, mas o Ben decidiu ligar no pino 2 do Arduino e com isso também precisamos configurar o pino 2 como entrada, pra ler o pulso que vier de lá.</p>
<p>A forma de ligar um evento no Arduino é criando uma interrupção pro clock. Quando o clock pulsa e enviar um sinal elétrico no pino 2, isso vai interromper o sistema e chamar essa função onClock que estamos definindo. Se você já programou front-end ou javascript já viu o mesmo conceito que é orientação a eventos. O conceito existe desde a invenção dos computadores, só pra saber.</p>
<p>Agora movemos o código do loop de leitura pra dentro do evento onClock.</p>
<p>Aproveitamos pra declarar também os pinos de 39 a 53 onde vamos ligar os pinos de dados D0 a D7 do CPU, pra poder monitorar o que acontece lá também.</p>
<p>Agora copiamos o mesmo código que lê dos pinos de endereço da CPU pra ler os pinos de dados da CPU, é o mesmo loop, só muda os números dos pinos.</p>
<p>Também copiamos o mesmo código de inicialização pra configurar os pinos 39 a 53 como INPUT pro Arduino receber bits.</p>
<p>E copiamos o mesmo código no evento de onClock pra ler os pinos de dados e imprimir na tela. Só que ficar mostrando os 24 bits de zeros e uns dos pinos não ajuda muito pra saber o que tá acontecendo. Mesmo gente experiente não consegue ler um monte de blocos de bits e entender exatamente o que tá rolando.</p>
<p>Então vamos converter de bits pra hexadecimal e imprimir do lado. Pra fazer isso inicializamos uma variável com número 0, dai pra cada pino que lemos nesse <code>for</code>, damos um shift pra esquerda e adicionamos o novo bit no final, faz shift pra esquerda, adiciona no final, faz shift e adiciona, e é assim que se monta um número lendo do binário.</p>
<p>Fazemos a mesma coisa com os pinos de dados.</p>
<p>Agora vamos usar uma função que é comum em quase todas as linguagens que serve pra formatar números em strings. Queremos formatar o número em uma string hexadecimal de 4 bytes pra representar o endereço de 16-bits, e como 2 bytes o valor de dados.</p>
<p>Os pinos de dados podem tanto receber dados quanto enviar dados e pra isso serve esse outro pino do lado, pra dizer qual o modo, se é read ou write. Então ligamos ele no pino 3 do arduino pra sabermos se o CPU está querendo ler ou escrever.</p>
<p>Como fizemos com os outros pinos, declaramos no codigo como 3 que é onde vamos ligar o pino RW do CPU. Dizemos ao Arduino pra ler desse pino, ou seja, input. Quando ler do pino, se devolver true escrevemos &ldquo;r&rdquo; pra leitura, se devolver false, escrevemos &lsquo;W&rsquo; pra indicar write.</p>
<p>Pronto, agora iniciamos o clock. No monitor podemos ver que o CPU tá cuspindo alguma coisa nos pinos de endereço e de dados mas tá difícil de entender o que tá acontecendo. Isso porque não tem nada enviando dados ao CPU, daí quando ele tenta ler alguma coisa dos pinos de dados vem lixo, e tenta rodar esse lixo e fica soltando lixo, por isso parece aleatório.</p>
<p>Agora que temos o CPU ligado e monitorado, vamos enviar o primeiro dado de verdade pra ele. Lembrem-se, enviar ou receber dados é literalmente aumentar ou diminuir voltagem nos pinos. Envie 5 volts no pino D0 e o CPU vai ler o bit 1 do pino D0, corte a voltagem do pino D1 e o CPU vai interpretar como 0 no pino D1. Então se quisermos mandar um valor constante nos pinos de dados, podemos ligar resistores. Se quisermos o bit 0 ligamos o resistor no terra, se quisermos o bit 1 ligamos o resistor nos 5 volts da placa. E com isso configuramos o binário 1 1 1 0 1 0 1 0.</p>
<p>Voltando ao monitor, resetamos e veja que na última coluna vem <code>ea</code> que é o hexadecimal do binário que acabamos de configurar. Como não mandamos o CPU gravar, o pino de Read/Write tá só Read, e note como nos pinos de endereço, a cada clock ele vai incrementando o endereço. Vamos ver com mais calma passo a passo.</p>
<p>Esse clock simples do Ben tem a opção de pausar e manualmente soltar um pulso de clock de cada vez clicando num botão. Segundo o manual do processador os primeiros 7 clocks é uma rotina de inicialização do CPU então podemos ignorar.</p>
<p>Logo em seguida o contador de programa carrega o vetor de reset dos endereços FFFC e FFFD. Como o único dado que conectado é o 0xEA dos resistores, ele vai ler o endereço 0xEA e 0xEA e montar o endereço 0xEAEA que é pra onde o CPU vai procurar a primeira instrução do programa pra executar.</p>
<p>Ele vai colocar 0xEAEA nos pinos de endereço e a única resposta que  vai ter continua sendo 0xEA, de novo porque é o único dado fixo nos pinos de dados. O Ben escolheu 0xEA de propósito porque é um opcode válido do 6502, se olharmos qualquer documentação dessa CPU vamos descobrir que é o opcode NOP ou No operation, que é literalmente um comando que não faz nada por 2 ciclos.</p>
<p>Quando o primeiro NOP terminar, o contador de programa aponta pro próximo endereço que é 0xEAEB e de novo, nos pinos de dados só vai ler só 0xEA. Daí o CPU executa NOP e o contador de programa agora vai pra OxEAEC e assim por diante infinitamente.</p>
<p>Até aqui você acabou de aprender alguns dos principais elementos que tem em qualquer CPU. Primeiro que ele tem pinos pra enviar endereços, segundo que ele tem pinos pra enviar e receber dados, que ele tem um contador de programa que aponta pra endereço da próxima instrução do programa. Terceiro que instruções e dados são tudo binário, e que não existe diferença entre um byte de dados e um byte de instrução.</p>
<p>Mas só deixar um valor fixo com resistores não é muito interessante. Pra ficar melhor, precisamos de alguma coisa que consiga enviar uma sequência maior de bytes, formando um programa.</p>
<p><a href="https://www.youtube.com/watch?v=yl8vPW5hydQ"target="_blank" rel="noopener">https://www.youtube.com/watch?v=yl8vPW5hydQ</a> (part 2)</p>
<p>Pra isso o Ben escolheu uma EEPROM do mesmo fabricante desse clone de 6502 que estamos usando, pra facilitar. Vale explicar o que é isso. RAM acho que todo mundo tem noção do que é, o que chamamos de memória. Podemos escrever e ler dados da memória e quando o computador desliga o que está na RAM apaga. Daí temos ROM que é sigla de Read only memory, ou seja é uma memória que só dá pra ler o que tem dentro e não é possível sobrescrever nem apagar.</p>
<p>Só pra ter um modelo simplificado na cabeça, pense na ROM como se fosse uma grade. Pense em cada linha dessa grade como um endereço, de 0 até X. E digamos que cada linha dessa grade tenha 8 cruzamentos. Quando um cruzamento está aberto, podemos dizer que é o bit 1, quando um cruzamento estiver queimado, pense queimado mesmo, ou seja que não tem como recuperar e mudar o bit. Podemos dizer que é o bit 0. O conteúdo de uma ROM é fixo, não tem como regravar. Você só consegue enviar endereços e ler o que estiver nesses endereços.</p>
<p>Agora, EEPROM é um circuito mais complicado. Quando ligamos num computador ele simula uma ROM, ou seja passe um endereço e ele devolve um dado. Porém, usando um gravador especial, ele permite que a gente escreva nela. Na prática uma evolução desse EEPROM são chips de memória flash usados em pendrives ou SSDs por exemplo. EEPROM é sigla pra Electronically-erasable programmable ROM. Nos anos 90 eu usava EEPROMs pra gravar um boot em computadores da rede da faculdade pra bootarem dessa ROM e se conectarem na rede pra baixar o sistema operacional de verdade. Dá pra usar EEPROMs pra muita coisa. Seria como bootar de um pendrive hoje mas a vantagem na época é que era difícil de pular esse boot obrigatório.</p>
<p>Diferente de RAM, mesmo quando desligarmos a força, o conteúdo não vai se apagar, igual um pendrive. Tanto nos videos do Ben quanto aqui vamos ficar toda hora chamando isso de ROM porque é mais curto, mas lembre-se que é uma EEPROM. E no caso é um modelo até grandinho, de 32 kilobytes.</p>
<p>Se olhar o diagrama vemos que a pinagem é muito simples, ele tem pinos de IO0 a IO7 pra devolver dados e pinos de A0 a A14 pra receber endereços. E porque vai só até A14 e não até A15. Porque ele só tem 32 kbytes que é metade de 64 kbytes. Com 16-bits conseguimos contar de 0 até 64kbytes, com 15-bits, que é metade conseguimos mapear os 32 kbytes que tem nessa ROM, por isso não precisa de mais 1 bit. Tão começando a se acostumar com essa história de bits? Basta associar bit com um pino num chip desses.</p>
<p>Repetindo, com 16-bits podemos mapear de 0x0000 até 0xFFFF.
Prestem atenção no comprimento dessas palavras. Cada dígito representa 4-bits. Cada 2 dígitos é um byte. 16-bits é a mesma coisa que 2 bytes, por isso 4 dígitos. Metade de 16-bits é só 1 bit a menos, o que nos dá os endereços de 0x0000 até 0x7FFF.
Se o CPU tentar mandar um endereço de 0x8000 pra cima, o primeiro bit é ignorado e o restante é igual à primeira metade, então ele devolve a mesma coisa que devolveria com o endereço de primeiro bit 0. Por exemplo, se ele pedir o endereço 0x8004 é a mesma coisa que pedir o endereço 0x0004.</p>
<p>Note outra vantagem de usarmos hexadecimais. Como eu sei exatamente qual é o numero do meio entre 0 e 65353?
Eu disse que pra dividir um numero por dois é só fazer shift pra direita, daí 1111, por exemplo, vira 0111. E esse é o numero do meio. E em hexa? qual a metade de F? É 7 porque F é 15, então metade de FFFF é 7FFF. Que é até onde vai o endereço nessa ROM. Tão vendo porque usamos hexa? Com decimal não é intuitivo de trabalhar. Aliás, em decimal, 7FFF em hexa seria 32767.</p>
<p>Como o CPU consegue entender qualquer endereço entre 0x0000 e 0xFFFF não queremos que a ROM responda em todos. Aqui é uma decisão de engenharia, mas pra manter a lógica simples, podemos escolher fazer a ROM responder à primeira metade dos endereços ou à segunda metade. E o Ben resolveu fazer responder à segunda metade, ou seja, quando o primeiro bit do endereço for &ldquo;1&rdquo;. Isso porque já vimos que o CPU procura logo de cara nos endereços FFFC e FFFD que ficam na segunda metade dos endereços, logo é mais conveniente mapear direto pra lá.</p>
<p>Pra fazer isso funcionar queremos responder somente a endereços que comecem com primeiro bit sendo &ldquo;1&rdquo;, ou seja, quando o pino A15 por &ldquo;1&rdquo;. Aliás, se vocês não perceberam a ordem dos bits é o inverso da numeração dos pinos, acostumem-se com isso. Além disso, pra ROM ficar habilitado ele precisa receber baixa voltagem no pino de chip enable ou CE. É como um botão de liga e desliga da ROM, pra ele responder precisa estar habilitado também.</p>
<p>Isso é simples, colocamos um inversor entre o A15 e CE assim quando A15 estiver em alta voltagem, ou seja, representando o bit 1 - porque queremos todos os endereços que começam bom 1 -, o inversor vai reverter e enviar baixa voltagem, ou 0, pro CE que vai habilitar a ROM. O resto dos pinos podemos ligar direto da CPU pra ROM um pra um, A14 da CPU pra A14 da ROM, D0 da CPU pro IO0 da ROM e assim por diante.</p>
<p>Coisas como esse inversor que mencionei pode ser montado com uma porta lógica chamada NAND. Eu mencionei portas lógicas no começo do episódio de Supremacia Quântica, mas o canal do Ben tem videos detalhados explicando como transistores funcionam e como podemos montar portas lógicas como NAND num breadboard também. Pro protótipo de hoje ele usa um chip pré-pronto com portas NAND embutidas. Mas entenda que todos os chips que você estão vendo são montados com transistores. Imagine transistores como peças de lego e chips como sendo um conjunto de transistores conectados.</p>
<p>Como falei antes, é com a relação de como montamos circuitos com transistores que nasce a base da tal &ldquo;lógica&rdquo;. Como conectamos portas pra chegar no resultado que precisamos. Neste exemplo simples, queremos uma resposta sempre que o pino A15 estiver em alta, com bit 1. Mas a ROM precisa do pino CS em baixa, ou bit 0 pra ativar. Portanto, colocamos alguma coisa que inverte o sinal entre elas, um inversor ou porta NAND, e assim vamos completando o quebra cabeça.</p>
<p>Com a ROM conectada e pronta precisamos carregar algum programa nela. Pra isso o Ben vai usar Python pra criar um arquivo binário. Poderíamos usar o próprio <code>xxd</code> pra escrever na mão. Mas qualquer linguagem de programação também consegue fazer a mesma coisa, use o que você achar mais fácil, mas vamos seguir no código dele que é simples de entender.</p>
<p>Relembrando que antes a CPU recebia somente o valor fixo dos registradores, o  hexadecimal 0xEA que é a instrução ou opcode NOP. Vamos gerar um binário que devolve exatamente a mesma coisa.</p>
<p>Primeiro declaramos uma variavel que vai ser um array de bytes. O conteúdo vai ser 0xEA 32 mil 768 vezes que é o total de bytes que dá exatamente 32 kilobytes.</p>
<p>Agora vamos abrir um arquivo pra escrever em modo binário. Com o arquivo aberto, escrevemos o array de bytes direto e isso vai gerar o arquivo &ldquo;rom.bin&rdquo;</p>
<p>Se fizermos um hexdump podemos ver que só tem 0xEA. Quando tem um asterisco é a ferramenta <code>hexdump</code> querendo dizer que o mesmo conteúdo se repete até o fim, que é o endereço 7FFF, o último em 32 kilobytes.</p>
<p>Lembrando que a CPU vai pedir endereços a partir de 0x8000 mas como a ROM não recebe o primeiro bit, só recebe os 15-bits restantes do endereço, então o endereço 0x8000 vira automaticamente endereço 0, como já expliquei.</p>
<p>Agora podemos pegar o arquivo binário e usar o programa <code>minipro</code> que envia os bits pro gravador de EEPROM. Isso é só um detalhe relevante se você for usar um gravador de EEPROM como na demonstração do Ben, mas é assim que se grava, se você tinha curiosidade.</p>
<p>Finalmente colocamos a EEPROM de volta na placa. E ligamos o Arduino de novo pra monitorar.</p>
<p>Podemos usar o mesmo script do Arduino de antes e ligar o Monitor e como esperado tá recebendo só 0xEA.</p>
<p>Pra ver passo a passo damos um reset e pausamos o clock. Agora podemos ir passo a passo. Os primeiros 7 clocks é a inicialização do CPU, depois disso ele lê dos endereços fffc e fffd e lê 0xea como antes. E com isso ele pula pro endereço 0xeaea e continua lendo só 0xea. Até aqui replicamos exatamente o que os registradores estáticos faziam. Vamos fazer algo um pouco mais útil.</p>
<p>Voltamos pro programa em python e depois de ter o array de bytes com 0xea, acessamos a posição 0x7ffc e colocamos o valor 0 e na posição 0x7ffd colocamos o valor 0x80. Duas coisas, lembram na explicação de binário que eu mostrei exemplos de Javascript, que expliquei que podemos acessar posições num array tanto com números decimais quanto com números direto em binário? Escrever 0x7ffc é a mesma coisa que escrever 32764.</p>
<p>E repetindo, por que 0x7fffc e não 0xfffc que é o que o CPU vai procurar? Porque a CPU vai mandar o endereço 0xFFFC pra ROM, mas o primeiro bit é ignorado? Lembra como o endereço 0x8000 vira 0x0000? Então é só fazer 0xFFFC - 0x8000 e voilá temos 0x7FFC. Não entendeu? 0xF em hexa é 15, subtrai 8 e temos 7.</p>
<p>Agora uma coisa que eu deixei pra explicar só agora. O CPU vai pedir o valor do endereço 0xFFFC e vai encontrar 0x00 e daí vai pedir 0xFFFD e vai encontrar 0x80 e esses dois valores vão formar o endereço 0x8000. Essa forma de ordenar os bytes &ldquo;ao contrário&rdquo; se chama Little endianess. Outros processadores como um Intel ou AMD ou mesmo os ARM de celulares também são little endian. O processador IBM PowerPC dos Macs anteriores a 2006 eram Big Endian, então os bytes iam aparecer na ordem natural que nós humanos lemos. Mesmo os processadores ARM de hoje são bi-endian, quer dizer que ele suporta ambos mas a maioria é usado como little endian.</p>
<p>Parece meio estranho, porque eu ia querer ler números de mais de um byte em ordem reversa? Na realidade parece estranho porque a gente lê da esquerda pra direita, mas quando implementamos em hardware é mais simples ler da direita pra esquerda, requer menos ciclos pra fazer contas. Sem entrar em detalhes do hardware vamos dar um exemplo onde a ordem natural pode não facilitar nossa vida. Imagine datas no formato brasileiro com dia, mês e ano. Mande um computador ordenar. Podemos usar o comando <code>sort</code> que tem em todo linux. Porém coloque as mesmas datas agora em formato japonês, que é ano, mês e dia e tente ordenar da mesma forma. E veja só, só de mudarmos a ordem dos elementos das mesmas datas, a ordenação funcionou muito mais fácil.</p>
<p>Aliás isso é um insight que todo programador precisa ter. Boa parte do que é programação é retrabalhar os dados pra ficar mais fácil de computar o que queremos. Little endian no nível do processador é algo parecido na hora de fazer contas. Pra agora só assuma que quando você vê dois bytes que representam endereço de 16-bits, eles vão estar invertidos e 0x0080 vai ser 0x8000.</p>
<p>01012000
01031998
02071999
03042003
05052005</p>
<p>20030403
19980301
19990702
20050505
20000101</p>
<p>Uma vez eu justamente precisei fazer um pequeno protocolo de rede binário pra trafegar dados entre computadores Intel e PowerPC. Acho que eu estava usando um Macbook G4 antigo. E eu recebia lixo no lado do Intel. Até que me dei conta &ldquo;puts, PowerPC é Big Endian&rdquo;, daí eu inverti os bytes no meu protocolo antes de mandar e do lado do Intel passou a receber direitinho. Você não vê isso porque se o único protocolo que já usou foi HTTP na camada de baixo no nível do TCP ele se vira pra reverter os bytes se trafegar entre processadores com endianess diferentes. Se você já usou algo como Google Protobufs, a biblioteca se encarrega de converter se precisar.</p>
<p>Isso tudo explicado, vamos fazer um programinha agora. Lembrando, um CPU tem registradores e pode guardar dados de 8-bits, ou 1 byte neles. Pense em registradores como se fossem variáveis globais numa linguagem de programação qualquer. Elas vão ser uma ordem de grandeza mais rápidas de acessar do que se tiver que sair do CPU e pedir memória pra um chip de RAM por exemplo. Registradores que podemos usar no 6502 incluem o A, X, Y e outros. Cada registrador vai ter um comando de LOAD pra jogar um valor nele e um comando de STORE pra tirar o valor do registrador e jogar em algum outro lugar na memória, se tiver RAM por exemplo.</p>
<p>Vamos só fazer um LOAD em A que é o opcode LDA. Assim como em linguagens modernas existe o conceito de polimorfismo, ou seja, a mesma função responder pra argumentos diferentes, no nível de máquina o mesmo comando LDA na verdade é representado por diferentes hexadecimais pra cada variação de argumentos. Por exemplo existe a instrução 0xAD, mas a versão que queremos é a que aceita só um valor simples e não um endereço de memória como argumento, então o hexadecimal da instrução LDA queremos é o 0xA9.</p>
<p>De volta ao python se acessarmos o primeiro valor do array, posição 0x00, colocamos o valor 0xA9 que é o comando LDA e na sequência colocamos o argumento dessa instrução que pode ser qualquer valor de 1 byte, aqui colocamos 0x42 arbitrariamente. A instrução completa são esses dois bytes. Dependendo da instrução pode ter mais argumentos, usando mais bytes na sequência. O CPU sabe quantos bytes cada instrução precisa. Tudo tem tamanho fixo em bytes na CPU, e assim ele sabe qual byte é uma instrução e quais bytes são dados.</p>
<p>Como agora temos um valor no registrador A podemos fazer alguma coisa com isso, tipo escrever nos pinos de dados. Pra isso usamos o STORE from A ou Grave a partir de A, que é o comando STA e ele recebe um argumento de 2 bytes que é o endereço pra onde queremos enviar o valor de A.</p>
<p>Então acessamos a posição 2 do array e colocamos o binário 0x8D que é a instrução STA. Agora nas posições seguintes queremos enviar esse dado pro endereço 0x6000, como já expliquei queremos o inverso disso então na posição 3 do array colocamos 0x00 e na posição 4 colocamos 0x6000.</p>
<p>Essa sequência de 3 bytes vai fazer o seguinte: escrever o valor que está no registrador A nos pinos de dados, mudar o bit no pino de read write pra escrever e mudar os pinos de endereço pra 0x6000.</p>
<p>Rodamos o script python pra gerar o novo binário, gravamos na EEPROM com o minipro de novo, recolocamos a ROM na placa e ligamos a energia e o monitor do Arduino.</p>
<p>Fazemos reset com clock pausado, e vamos manualmente clock a clock. Mesma coisa de antes, ignoramos os 7 primeiros clocks que é a inicialização. A CPU pede os valores de 0xfffc e 0xfffd que puxa da ROM o endereço 0x8000, esse é o reset vector e a CPU pede o que tem nesse endereço, que sabemos que é o endereço 0 da ROM e tem a instrução LDA que é 0x9a.</p>
<p>O contador vai incrementando a partir de 0x8000 e pede o que tem em 0x8001 que é o valor 0x42. A instrução está completa e a CPU grava no registrador. Agora o contador pula pra 0x8002, e de lá tem a próxima instrução STA que é 0x8d. O CPU vai incrementar o contador e ler os proximos dois bytes, que vai ser primeiro 0x00 e depois 0x60, esse é o endereço 0x6000.</p>
<p>Tudo funcionando conforme esperamos mas ao configurar os pinos pra gravar, não tem ninguém no barramento que responde a essas ordens, e nada acontece. No próximo ciclo esse dado vai sumir do barramento. Precisamos de alguma coisa pra segurar esse dado enquanto a CPU fica livre pra fazer outras coisas. Esse seria um componente chamado &ldquo;Latch&rdquo; que acho que podemos traduzir pra &ldquo;Tranca&rdquo;. Pra esse protótipo o Ben escolheu um chip que foi feito pelo mesmo fabricante desse clone de 6502 e é um adaptador que serve pra muitas coisas incluindo trancar alguns valores. Pense como se fosse um mini-memória.</p>
<p>Por acaso, os pinos desse adaptador são muito parecidos com o da ROM que já estamos usando, e tem espaço pra gravar até 2 dados de 8 bits no que ele chama de &ldquo;Portas&rdquo;. Tem uma Porta A de 8-bits e uma Porta-B de 8-bits. Por isso você tem pinos de PA0 a PA7 e pinos PB0 a PB7. A essa altura vocês já devem ter entendido a idéia de pinos serem cada um 1 bit né?</p>
<p>Diferente da ROM não precisamos de 16 pinos pra endereço porque esse chip só consegue armazenar no 2 valores. Sabemos que queremos ativar esse chip quando passarmos endereços que comecem com 0x60 que em binário é 0110 0000. Resumindo o que o Ben explica no video dele, ele vai ligar os endereços A15 a A13 com os pinos de chip select que são CS1 e CS2 e colocar um inversor e outra porta NAND no meio deles pra configurar o chip corretamente. Os detalhes não importam pra onde eu quero chegar, mas recomendo que assistam o episódio dele pra entender os detalhes.</p>
<p>O importante é entender que quando a CPU colocar endereços como 0x6000 ou 0x6001 ou 0x6002 e assim por diante, esse chip vai entender os primeiros 3 bits, ligar e receber o dado de 8 bits dos pinos de dados e gravar na Porta A ou Porta B. Quem indica a operação é o segundo byte do endereço. Entenda, o CPU chama os pinos de endereço, mas são bits, podemos ligar esses pinos com qualquer outra coisa pra montar nossa lógica.</p>
<p>No chip de Tranca, segundo a documentação ele espera receber 4 bits de configuração em 4 pinos registradores diferentes. Podemos ligar esses pinos aos pinos A0 até a4 que são os últimos bits do endereço de 16-bits. Se mandar 0 0 1 0 que é o número 2 ele direciona pra Porta B e se depois mandar 0 0 0 0 ele manda gravar efetivamente na Porta B, portanto a CPU precisa mandar o endereço 0x6002 pra ao mesmo tempo habilitar e configurar a direção e depois mandar o endereço 0x6000 pra de fato gravar o que estiver nos pinos de dados na Porta B. Parece complicado mas é bem simples, vamos ver.</p>
<p>Pra ver isso em funcionamento podemos ligar alguns LEDS nos pinos da Porta B. Assim quando o valor for trancado, ele vai ligar os LEDs correspondentes.</p>
<p>De volta ao python mas antes de continuar, é uma boa hora pra refatorar esse código pra ficar mais legível. É assim que se programa, primeiro fazemos a coisa mais simples que funciona primeiro. Agora vemos padrões no código que dificulta continuar, então pensamos em formas de tornar o código mais fácil de manusear.</p>
<p>Um das formas de fazer isso é definir um novo array de bytes em cima do array cheio de 0xEA&rsquo;s, esse array vai conter nosso programa exatamente igual antes. É só colocar os bytes que estávamos posicionamento manualmente embaixo, em sequência em cima e apagar o que tava embaixo.</p>
<p>Agora o array de ROM vai ser esse novo array de código mais o array preenchido com 0xea, só que em vez de ser vezes 32768, vai ser 32768
menos o comprimento do novo array de opcodes. Assim o array do binário vai ter exatamente o mesmo comprimento de antes e isso é importante porque tem que ser tudo preciso.</p>
<p>Escrever tudo numa linha só ou dividir em multiplas linhas pro python não faz nenhuma diferença, só faz diferença pra gente conseguir ler mais fácil. E de proposito colocamos dois bytes numa linha porque o primeiro é o hexa da instrucao LDA seguido do seu argumento. E na segunda linha colocamos 3 bytes onde o primeiro é a instrucao STA seguido de 2 bytes que formam o endereço pra onde gravar o conteudo de A.</p>
<p>Esse código é exatamente igual o que tínhamos antes, mas agora temos a Tranca que exige primeiro uma instrucao pra dizer a direçao pra saber qual porta usar. Então precisamos mandar o endereço 0x6002. Mas também precisamos mandar algum dado pelos pinos de dados. Se mandar tudo 0 ele habilita a porta B pra ser input e se mandar tudo 1 ele habilita a porta B pra ser output e gravar o que receber.</p>
<p>Enviar tudo 1 em 8 bits é o hexa 0xFF. Então mudamos o nosso array de código pra em vez de carregar aquele 0x42 vamos carregar com o número 0xFF. E em vez de mandar esse valor pro endereço 0x6000, vamos mandar pro 0x6002, e com isso setamos a direção do chip pra porta B e pra ela ser output.</p>
<p>Na sequência podemos fazer o que queríamos antes, travar um valor, digamos o hexa 0x55. Agora é só mandar a instrução STA pro 0x6000. Por acaso 0x55 em hexa é 0101 0101 e isso vai ligar os leds alternando o primeiro apagado, o segundo aceso e assim por diante.</p>
<p>Pra ficar mais legal podemos agora repetir os comandos, e mandar armazenar 0xaa em hexa que é 1010 1010 em binario, alternando os bits com o valor anterior, dai os leds apagados vao acender e os acesos vao apagar. Só uma brincadeira pra mostrar como podemos controlar alguns LEDs.</p>
<p>Pra finalizar seria legal colocar essas duas sequências num loop pros leds ficarem piscando sem parar, repetindo essas sequências. E a forma de se fazer loop, segundo a documentação do 6502 é com um jump que é a instrução JMP. Ele manipula o contador de programa pra apontar pra outro endereço qualquer, fazendo o CPU continuar incrementado de lá.</p>
<p>Então podemos fazer JMP pra 0x8000 que é o endereço 0 na ROM e começar do zero, mas não tem necessidade de reiniciar tudo. Um jump pro endereço 0x8005 é suficiente, ou seja, pulando os primeiros 5 bytes. Agora podemos rodar o script, gerar o novo binário, gravar na EEPROM, ligar e testar. E pronto, LEDs piscando como esperado.</p>
<p><a href="https://www.youtube.com/watch?v=oO8_2JJV0B4"target="_blank" rel="noopener">https://www.youtube.com/watch?v=oO8_2JJV0B4</a> (parte 3)</p>
<p>Se você chegou até este ponto e tá entendendo mais ou menos, no mínimo já deve tá ficando meio ansioso de ver esse monte de bytes num array. Neste ponto o Ben resolveu explicar uma forma menos feia de gerar esse binário. Obviamente ninguém escreve código em binário direto assim.</p>
<p>Mas considere por um instante que nos anos 50 pra trás era exatamente assim que precisava escrever. E a forma de fazer isso era com fios. Sabe os pinos de dados e endereço que ligamos registradores e agora ligamos a ROM? Antigamente era um patch panel. Lembra aqueles seriados ou filmes antigos dos anos 50 que mostra uma telefonista conectando uma ligação colocando ou tirando fios de um painel? É algo parecido. Configura os bits e manda pro computador, um conjunto de bits de cada vez.</p>
<p>Se você já leu alguma mini biografia do Bill Gates e do Paul Allen, vai lembrar que o primeiro computador comercial pra qual eles escreveram Basic foi o Altair 8800 que vinha com o processador Intel 8080 de 8-bits. Isso foi um ano antes de aparecer o processador Motorola 6800 que foi a base pro 6502 que estou mostrando aqui. Se você nunca viu um Altair veja esta foto, tá vendo esses switches no painel? A etiqueta em cima deles tá escrito D0 a D7 e embaixo A0 a A15. Agora você já sabe pra que serve esses switches.</p>
<p>Você tinha que escrever bit a bit subindo ou descendo esses switches, um a um os bits de dado e os bits de endereço e depois puxar o switch de &ldquo;DEPOSIT NEXT&rdquo; que tem nesse painel pra registrar na memória e fazer isso instrução a instrução, que é o equivalente do nosso array de bytes. Se você tá achando tedioso o que estamos fazendo, imagina escrever um programa inteiro só com esses switches, bit a bit, e sem errar nenhum bit. Essa é a verdadeira definição de &ldquo;escovar bits&rdquo;. Felizmente os computadores caseiros que vieram depois ganharam teclados e monitor e  isso facilitou ordens de grandeza a programar.</p>
<p>De qualquer forma, havia uma opção um nível acima de escrever binários direto, e é escrever em Assembly. E é assembly com &ldquo;y&rdquo; no final, que é escrever usando mnemônicos como usar a palavra LDA em vez de escrever o binário pra 0xa9.</p>
<p>Em vez de escrever direto 0xa9 0xff podemos escrever LDA # $ F F. O dólar indica que estamos escrevendo um número hexa e o hash significa que é um valor imediato e não um endereço. Quando compila sem o hash apesar do mnemonico ser LDA, temos um tipo de polimorfismo como já expliquei. Se passarmos um valor com hash vamos escolher o 0xa9, mas sem o hash, FF é um endereço e o opcode é diferente, o 0xa5.</p>
<p>Continuando em vez de 0x8d 0x00 0x60 podemos escrever STA $6000, ou seja, também não precisamos nos preocupar com a inversão de little endian como falei antes e escrever na ordem natural de leitura humana.</p>
<p>Nas linhas seguintes fazemos lda 55, sta 6000,
lda 11, sta 6000, jmp 8005</p>
<p>É exatamente a mesma coisa que fizemos no binário. Pra declarar que o início do programa é o endereço 0x8000 colocamos um indicador no começo do arquivo <code>.org 8000</code> que declara a origem. Lembre-se, na ROM o endereço inicial vai ser 0x00 mas do ponto de vista do CPU esse é o endereço 0x8000.</p>
<p>No final do código colocamos um novo <code>.org fffc</code> que é o primeiro endereço que o CPU sempre vai pedir pra buscar qual é o próximo endereço onde tem a primeira instruçao do programa, já repetimos isso várias vezes. E aí colocamos um word 0x8000 que indica o endereço inicial pro CPU buscar. Word é como chamamos um conjunto de 2 bytes.</p>
<p>Salvamos esses comandos num arquivo texto e agora podemos passar isso pra um Assembler, com &ldquo;er&rdquo; no final, que vai traduzir esse texto em binário. Em inglês Assembly que termina em &ldquo;y&rdquo; significa &ldquo;montagem&rdquo;, que é a instrução de montagem, ou o código-fonte. E Assembler que terminar com &ldquo;er&rdquo; é o montador, que vai pegar essa instrução de montagem e montar o binário propriamente dito.</p>
<p>Pra gerar o binário correto precisamos do Assembler específico pro 6502. Na maioria dos Linux basta instalar o pacote <code>vasm</code>. Pra quem tá em Windows ou Mac vou deixar o link do site nas descrições abaixo e o video do Ben explica em detalhes como usar.</p>
<p>Existem vários assemblers diferentes pra família 6502, do Apple II é diferente do Atari que é diferente do 6502 genérico. O assembler que precisamos se chama <code>vasm6502_oldstyle</code>. Se abrirmos o binário que ele gera com hexdump e compararmos com o nosso produzido com o script de python, vemos que é idêntico ao array de bytes que estávamos escrevendo na mão.</p>
<p>Pra completar os 32k vemos que faltou só 2 bytes no final, pra isso só adicionamos um novo word no fim com 2 bytes pra completar. Mas agora que estamos escrevendo em Assembly podemos usar funcionalidades do Assembler Vasm que melhoram um pouco nossa qualidade de vida de programação em baixo nível. A maioria dos Assembler implementam macros e outras coisas pra evitar que a gente tenha que ficar fazendo repetições ou decorando endereços de cabeça.</p>
<p>Por exemplo, lembra que temos um JUMP pra um endereço fixo 0x8005? E se colocarmos mais instruções antes disso? Toda hora ia precisar ficar atualizando esse endereço na mão. Em vez disso podemos adicionar um label e fazer o jump pular pra esse label e deixar o assembler calcular qual é o endereço certo.</p>
<p>A diferença no código é que precisamos sempre identar com espaços ou tab, e labels começam na primeira coluna sem identação. Só de fazer isso já nos livramos de um número hard-coded. Números mágicos como aquele sempre são ruins e em Assembly isso não é diferente.</p>
<p>Ainda dá pra melhorar o codigo. Podemos deliminar a região de reset com um label e só pra brincar, vamos mudar o valor 0x55 pra ser 0x50. No loop vamos tirar o que tinha antes e colocar uma instrução nova que é o ROR ou rotate right. Lembra o conceito de shift pra direita? Pense que 0x0050 é <code>0 0 0 0  0 1 0 1</code> daí quando der shift right vai virar <code>1 0 0 0  0 0 1 0</code> e a cada loop ele vai dar shift e rotacionar pra direita. Significa que vai ter só dois leds acesos a cada clock e eles vão se &ldquo;movimentando&rdquo; pra direita.</p>
<p>Podemos compilar com o Vawm pra gerar o binário, gravar na EEPROM e fazer o de sempre, colocar a ROM na placa, ligar força, resetar e ver se funciona. No caso ele tá rotacionando pra esquerda porque a placa tá de ponta cabeça no video, mas você entendeu.</p>
<p><a href="https://www.youtube.com/watch?v=FY3zTUaykVo"target="_blank" rel="noopener">https://www.youtube.com/watch?v=FY3zTUaykVo</a> (parte 4)</p>
<p>Com os LEDs fazendo o que esperávamos, sabemos que a trava está funcionando e gravando o dado na porta certa. Então podemos ir um passo além. Essa trava pode funcionar como um buffer pra uma tela e o Ben conseguiu uma tela de LCD simples. Novamente, vamos pular a configuração do hardware mas a parte importante é que ele recebe 8 pinos de dados como a ROM e o latch, e assim como o latch ele tem 3 pinos de controle onde podemos ativar configurações.</p>
<p>Em resumo o LCD pode ou receber instruções ou receber dados, que seriam as letras que queremos que apareça na tela. Instruções são coisas como limpar a tela, mudar a posição, configurar um cursor e assim por diante. O manual da tela tem os detalhes de cada configuração mas vou mostrar o valor que vamos mandar no Assembly. Pra receber dados vamos conectar os pinos de dados do LCD com os pinos da Porta B do latch, e pra configurar os 3 pinos de controle vamos ligar em 3 pinos da Porta A do latch.</p>
<p>Vamos refatorar o código de novo criando alguns simbolos ou constantes pro codigo ficar um pouco mais legivel. Criamos PORTB = 6000 e PORTA = 6001.
DDRB é a direção se vamos escrever na porta B o endereço é 6002
mas se quisermos escrever na PORTA temos que setar DDRA que é 6003</p>
<p>0xff significa tudo 1 em hexa, podemos deixar mais claro escrevendo o número em binario, bit a bit. Dizemos pro Assembler que isso é um número binário colocando % (porcento) no começo e ; (ponto e vírgula) é comentario, que não aparece no binário final.</p>
<p>Agora os pinos de controle do LCD, pra isso vamos setar só os primeiros 3 bits que são os que conectamos nos pinos de controle da tela. Agora é uma sequência específica desse modelo LCD, então não se preocupem tanto com os detalhes dessa parte.</p>
<p>Pra inicializar a tela começamos mandando 00111000. O primeiro bit seta modo 8-bits, o segundo seta 2 linhas na tela e o 3o  seta fonte de 5 por 8 pixels. Por fim um STA mandando pra PORTB.</p>
<p>Carregamos zero com LDA e mandamos Porta A com STA pra limpar os pinos de controle RS, RW e E. RS é o pino que seleciona se estamos mandando instruções ou dados pra tela, RW se estamos lendo ou escrevendo e E se tá habilitado pra receber comandos.</p>
<p>Agora queremos habilitar com LDA do valor E e depois desligamos esse enable. É uma sequencia bem burocrática mesmo, a maioria dos hardware tem alguma sequência de controle assim mesmo, mas é o que a documentação do LCD diz que precisa fazer. Mas já tá quase acabando.</p>
<p>Cada nova instrução que queremos mandar precisa copiar toda essa sequencia. Agora mandamos <code>0000 1110</code>. De novo 3 bits, mas agora é pra ligar o display, ligar o cursor e não fazer blink que é ficar piscando na tela.</p>
<p>Copiamos todo o bloco de controle e colamos embaixo. A última configuração é mandar <code>0000 0110</code> que é dizer pro LCD que pra cada nova letra que mandarmos é pra incrementar a posição e não fazer nenhum tipo de scroll.</p>
<p>E tem que copiar todo o bloco de controle de novo.</p>
<p>Finamente podemos enviar a letra que queremos escrever no LCD. Poderíamos dar LDA do número em binário ou hexa que representa a letra na tabela ASCII. Mas como estamos usando um Assembler, ele sabe converter sozinho então podemos escrever o string &ldquo;H&rdquo; entre aspas que ele se vira.</p>
<p>Daí mandamos pra Porta B com STA.</p>
<p>O bloco seguinte é um pouco diferente do que fizemos antes. Em vez de zerar os pinos de controle temos que ligar o pino que seleciona o registrador pra em vez de receber instrução, receber dado, entao configuramos com a constante RS que tínhamos declarado antes.</p>
<p>Na sequência, além de só habilitar com a constante &ldquo;E&rdquo;, tem que mandar junto o bit de RS. Pra isso usamos o operador bitwise de OR que é uma barra vertical. O que isso faz? Ele faz um OR bit a bit. De novo, isso é lógica. Na prática o resultado é mesclar os bits de RS e de E. Como RS é 0010 e E é 1000 o resultado vai ser 1010. Aqui valeria uma tangente mas como já está comprido só quero deixar um lembrete que essa é uma técnica muito usada especialmente pra quem mexe com protocolos de rede. Isso é como trabalhamos Bit Fields, procurem sobre isso no Google. É uma forma eficiente de enviar várias características de alguma coisa embutido num único byte em vez de ter um byte separado pra cada valor.</p>
<p>Por fim zeramos o bloco de loop pra quando chegar nele ficar num loop infinito e não fazer nada. Assim ele termina de escrever na tela e não faz mais nada.</p>
<p>Compila, grava a eeprom, e testamos. E aparece o &ldquo;H&rdquo; na tela como queríamos.</p>
<p>Pra escrever o resto das letras do hello world, por agora vamos fazer do jeito mais porco e sujo só pra ver o resultado rápido. Ou seja, copy e paste nervoso de toda a sequência da letra &ldquo;H&rdquo; pra cada uma das outras letras. Isso é obviamente péssimo de se fazer. Pra testar a primeira vez quando você ainda não sabe se vai dar certo tudo bem, mas obviamente vamos voltar e melhorar isso depois.</p>
<p>Vamos compilar e não só o código Assembly ficou longo, mas o binário reflete isso. Olha como o binario no hexdump ficou gigantesco com mais de 300 bytes só pra escrever um mísero hello world. Escrever código longo e sujo além de feio e ruim de manter, também gera instruções redundantes e aumenta o tamanho do binário.</p>
<p>Agora podemos transferir pra ROM, ligar na placa e, como esperávamos, temos um Hello World aparecendo. Se o objetivo for só escrever Hello World do jeito mais feio do mundo, já acabamos, mas podemos só acrescentar uma última coisa pra melhorar muito esse código.</p>
<p><a href="https://www.youtube.com/watch?v=xBjQVxVxOxc"target="_blank" rel="noopener">https://www.youtube.com/watch?v=xBjQVxVxOxc</a> (parte 5)</p>
<p>Vamos aumentar nosso arsenal de Assembly aprendendo o conceito de sub-rotinas, que é uma forma primitiva de funções. Aliás, pra quem já é programador e já ouviu falar do conceito de &ldquo;GO TO&rdquo; isso é o Jump pra um endereço fixo que mostramos com o mnemônico JMP. Mas nem em Assembly é obrigatório usar só jumps.</p>
<p>Pra refatorar esse codigo vamos criar uma subrotina chamada <code>lcd_instruction</code> que manda os comandos pra escrever os dados na tela depois do LDA. Toda aquela sequência que demos copy e paste várias vezes no começo do código.</p>
<p>Já sabemos que JMP pula pra um endereço fixo. O problema é que uma vez que você pula, não tem mais como voltar pra trás. Pra ter esse recurso podemos usar o opcode JSR que é Jump Sub Routine, que pula pra uma subrotina e grava o endereço atual do contador de programas. Daí, no final da subrotina usamos o comando RTS que é tipo <code>return</code> num Javascript, que vai pegar o ultimo endereço gravado pelo JSR e colocar de volta no contador de programas, e com isso o CPU continua na instrução logo depois do JSR.</p>
<p>Essa é a forma mais rudimentar de uma função em linguagem de máquina. Fazemos a mesma coisa criando uma subrotina <code>print_char</code>, chamamos JSR depois do load da letra e no final da subrotina colocamos RTS pra voltar pro byte seguinte da instrução de jump.</p>
<p>Depois de recompilar podemos comparar o tamanho dos binários, o anterior sujo tinha 333 bytes. Agora ficou bem menor em tamanho. Só que isso não sai de graça. A versão anterior era maior mas era pouca coisa mais rápido porque agora a CPU vai executar um monte de Jump e Return que não tinha antes. Cada uma dessas novas instruções usa clocks pra executar. Porém é aquele caso em que o ganho de performance não compensa a sujeira do código. Via de regra você sempre deve preferir código legível do que otimização prematura.</p>
<p>Isso faz parte do dia a dia de um programador: ter que escolher quando você escreve um pouco mais sujo pra ganhar performance e quando o ganho de performance é tão pequeno que não compensa. Não existe uma receita, só com experiência você vai saber. Mas via de regra você só toma essa decisão depois de conseguir testar o código e mensurar se o ganho vale a pena ou não. Nunca  ache que só porque um código rodou a primeira, já tá pronto e nunca mais vai mudar. Neste exemplo besta já refatoramos o código várias vezes, imagina num código grande de verdade.</p>
<p>Gravamos na ROM, testamos e &hellip;.  nada acontece, o programa parece que travou. Por que? Pra descobrir vamos ligar o monitor de Arduino de novo, resetar e colocar em clock manual pra ir linha a linha. Vamos acelerar direto pro suspeito mais óbvio que é o JSR.</p>
<p>Temos o LDA que vai configurar a primeira configuração pra tela. Em seguida temos o opcode 0x20 que é o JSR.</p>
<p>Deveríamos ver algo como um 0x20 e depois 0x5d e 0x80 representando o endereço 0x805d que é onde está a subrotina. Mas em vez em disso vemos essa sequencia estranha 5d 80 0e. Aí parece que volta ao normal continuando a instrucao com o 0x80 restante pra formar 0x805d. Ou seja, ele pula pra subrotina e podemos ver que ele segue normalmente.</p>
<p>Seguimos mais algumas instruções e chegamos no opcode 0x60 que é o RTS ou return, mas pra onde ele vai retornar? Deveria ser pra 0x800f que é o próximo endereço depois do último JSR em 0x800e.</p>
<p>Estamos vendo no monitor que o CPU tenta ler alguma coisa em 0x0123 e 0x0124. O lance é que JSR e RTS dependem de algum hardware que responde nesses endereços pra gravar o endereço de retorno, mas no momento nao temos nenhum hardware que responde.</p>
<p>Quando chama JSR ele tenta gravar nesses endereços, mas não tem ninguém esperando isso então o endereço de retorno vai pro limbo. Quando chega no RTS ele tenta recuperar o endereço, e não tem ninguém pra responder, então acaba tendo algum lixo nos pinos e o CPU assume que é um endereço e tenta pular pra lá. No caso veio um lixo 0x8d8d e o CPU vai pra lá mas não tem nada e o programa se perde.</p>
<p>Esses endereços 0x0123 0x0124 são específicos do processador 6502. Segundo a documentação, de 0x0100 a 0x01FF temos o Stack, uma pilha. E temos mais um registrador que ainda não mencionei, chamado SP. Todo CPU tem uma série de instruções implementadas em hardware como o LDA STA JSR e outros. Instruções como LDA grava no registrador chamado &ldquo;A&rdquo;. Até agora só vimos 2, o registrador A e o PC que é o contador de programa. E o ultimo registrador novo importante pra hoje é esse SP é o Stack Pointer, que guarda um endereço do topo de uma pilha. O SP é quem permite a existência do conceito de sub-rotinas e funções.</p>
<p>Stack ou Pilha é uma estrutura de dados. Lembra a tal fundação que eu sempre falo? Se você estudar estrutura de dados, vai descobrir a Pilha, uma das estruturas mais básicas e que você aprende logo no começo dessa matéria. Todo iniciante tem que saber o que é uma pilha e quando usar ela. Em resumo, é uma cadeia de elementos onde você vai colocando um elemento de cada vez no topo. E toda vez que pedir pra devolver um elemento, vai removendo do topo.</p>
<p>Essa pilha é um tipo particular chamado LIFO, Last In, First Out, ou seja, quem entra por último, sai primeiro. A vantagem de uma estrutura dessas é que basta guardar o endereço do topo. Depois que adiciona um elemento, decrementa o endereço do topo. Pra puxar da fila, devolve o dado do topo e incrementa o endereço no registrador SP. Quando falamos topo, não necessariamente começamos do zero e vamos subindo um a um. No caso do 6502 ele prefere iniciar no meio da pilha, em 0x0124 e vai subindo até 0 e quando chegar no zero ele rotaciona pro fim da pilha em 01FF e continua subindo.</p>
<p>Toda vez que você faz um JSR, ele vai acumular um endereço na pilha. Se você fizer mais que 255 jumps sem fazer um Return, ele vai estourar o tamanho da pilha e vai começar a sobrescrever o primeiro endereço gravado. É isso que se chama de transbordar da pilha ou, em inglês, Stack Overflow. Mesmo com uma pilha pequena dessas é muito difícil fazer 255 jumps sem retorno. Quando isso acontece normalmente é uma recursão mal feita. Recursão é uma sub-rotina que dá JSR pro começo dela mesma. É muito fácil um iniciante errar com recursão.</p>
<p>Outra coisa interessante de saber é que registradores são literalmente variáveis globais. Se você já trabalhou com C ou Javascript já deve ter ouvido falar de quão tenebroso uma variável global pode ser. Antes de fazer o JSR no nosso código, setamos o registrador A com um valor usando LDA, mas quando pulamos pra subrotina, dentro dela modificamos esse registrador A. Se depois que retornássemos o programa esperasse encontrar o mesmo valor original em A, teríamos problemas.</p>
<p>Como no nosso caso, depois que retornamos não faz diferença porque vamos escrever a próxima letra em cima do A mesmo, então tudo bem, mas se quiséssemos evitar algum efeito colateral num código maior, poderiamos primeiro empurrar o valor de A pro stack também, usando a instrução de push A que é PHA.</p>
<p>E antes de retornar com RTS podemos puxar o valor de A do topo do stack usando pull A ou PLA. Por garantia então devíamos usar isso no começo e fim de toda sub-rotina né? Mais ou menos, esse hardware antigo só tem 255 slots na pilha lembram? Se você souber que não vai estourar, pode fazer isso. Como eu falei, tudo é um trade-off. Se eu quiser garantias demais, os recursos podem acabam cedo demais e podemos acabar ganhando um bug de stack overflow sem querer.</p>
<p>Só por comodidade podemos mudar o registrador SP de 0x0124 pra 0x01FF pra começar do fim da pilha em vez do meio. Pra isso podemos procurar uma instrução como o LOAD A, mas nao tem LOAD pro registrador SP da pilha, e sim TRANSFER que é TXS, que transfere um valor do registrador X pro SP. E aqui aprendemos que existe um registrador chamado X além do A.</p>
<p>Assim como fazemos com o registrador A, primeiro usamos a instrução LOAD to X que é LDX com #$FF e daí transferimos X pra SP com TXS. Note que colocamos FF e não 0x01FF porque os endereços do stack sempre começam com 0 1 em binário entao na pratica estamos contando sempre de 00 a FF. Já expliquei que é um stack pequeno com 255 posições.</p>
<p>Tudo isso entendido, precisamos de algum lugar que tem espaço pra gravar 255 elementos de uma pilha e responde aos endereços de 0x0100 a 0x1FF. Nosso chip de trava só tem 2 portas então não serve. Chegou a hora de adicionarmos um chip de memória RAM. E fica outro insight, memória RAM pode ser opcional se o que você pretende fazer com um hardware são operações simples que sobrevivem só com registradores ou com uma trava simples.</p>
<p><a href="https://www.youtube.com/watch?v=i_wrxBdXTgM"target="_blank" rel="noopener">https://www.youtube.com/watch?v=i_wrxBdXTgM</a> (parte 6)</p>
<p>O Ben escolheu um chip de RAM que é muito parecido com a pinagem do EEPROM e da Trava que já estamos usando, então vamos pular a configuração do hardware que é a mesma coisa: liga força, liga terra, liga os pinos de dados e de endereço. Assim como todos os outros chips por acaso tem 3 pinos de controle que importam, um pra write enable que como o nome diz habilita escrita, um pra output enable e outro pra chip select. Write enable é fácil, porque o 6502 tem um pino read write que indica escrita, daí ligamos direto um no outro.</p>
<p>Assim como no caso do ROM e do LCD precisamos definir os endereços que a RAM vai responder. Sabemos que o stack precisa pelo menos dos endereços 0x0100 até 0x01FF. Mas vamos mapear o primeiro 1/4 dos endereços do topo que vai de 0x000 até 0x3FFF que é bem longe dos endereços 0x6000 que o LCD usa e do endereço 0x8000 que a ROM usa.</p>
<p>Isso é suficiente pra 16 kilobytes de RAM. Por acaso este chip moderno tem mais espaço que isso, então vamos desperdiçar um pouco neste protótipo. Pra usar tudo daria mais trabalho pra criar um mapeador de endereços. Mas lembre-se que um nintendinho tinha só 2 kilobytes de RAM e 2 kilobytes de memória de video. E no próximo episódio vou explicar porque não dava pra ter muito mais que isso mesmo.</p>
<p>Existe um pequeno circuito que precisa ser adaptado e vou pular essa explicação, mas em resumo a forma de conectar a RAM na CPU é ligar o A14 da cpu com o A14 da RAM cruzando com o output enable e o A15 da cpu de novo através de um inversor e um nand no chip select. O que precisamos saber é que isso vai habilitar a RAM quando o endereço começar com 0 0 que cobre os endereços que queremos. Pra entender porque a ligação em hardware é dessa maneira, veja o video do Ben.</p>
<p>Feito isso, agora temos RAM ligado e configurado respondem nos endereços que precisamos, então teoricamente já temos uma pilha disponível e com isso o programa deveria funcionar. Se ligarmos e dermos reset, de fato, agora o hello world aparece como deveria.</p>
<p>Tudo resolvido, vamos ligar o Arduino pra entender como o programa se comporta. Fazemos reset e deixamos o clock em manual de novo. Como sempre, vamos ter os 7 clocks pra inicializar a CPU e pulamos rápido até o ponto onde o programa faz o jump JSR que é o opcode 0x20.</p>
<p>Lembrando que configuramos o registrador SP pra começar de 0x01ff, que é o fim do espaço de endereços da pilha, e de fato vemos no monitor que tá escrevendo 0x80 no final do stack em 0x01ff. Daí o cpu atualiza o registrador SP pro próximo endereço ser 0x01fe e escrever 0x11 lá. 0x8011 é o endereço de retorno da subrotina.</p>
<p>Agora ele salta pra subrotina que tá em 0x8060, como já fazia antes, executa a sequência pra escrever a letra no LCE e chega na instrução RTS que é o opcode 0x60.</p>
<p>A instrução de retorno vai desempilhar o primeiro byte do topo da pilha, que está em 0x01fd, e lê 0x11. Daí decrementa o registrador SP e lê 0x80 e isso forma o endereço 0x8011 que é pra onde o programa tem que voltar. E com isso temos o suficiente de hardware pra fazer jumps o que garante que podemos programar praticamente qualquer coisa só com o que temos aqui.</p>
<p>Os dois registradores de controle de um CPU são o contador de programa ou PC que tem o endereço da próxima instrução ou byte que ele tem que ler. E o apontador de pilha ou SP que é o endereço do topo da pilha. Tendo esses dois controle você consegue executar uma instrução após outra e consegue dar jumps pra continuar executando de outro endereço e retornar pro endereço anterior. Ou seja, você tem o equivalente a um loop com WHILE numa linguagem de alto nível. Daí existem instruções de Branch e Compare que servem pra comparar valores e dar jumps dependendo das condições que você configurar, e isso é o equivalente a um IF numa linguagem moderna.</p>
<p>Com essas poucas instruções que vocês viram já dá pra ter o modelo da maioria das funcionalidades básicas de qualquer linguagem: loops, funções e ifs ou branches que não cheguei a mostrar mas acho que vocês já conseguem imaginar como seria. Na série do Ben tem um episódio extra depois que ele configurar o LCD onde ele troca o clock que está montado num breadboard por um clock de 1 Megahertz de verdade. Mas quando ele faz isso o programa pára de funcionar. Tudo funciona quando o clock é bem lento, mas quando o clock fica rápido demais, o CPU envia os comandos pro LCD rápido demais e o LCD não responde a tempo. Pra evitar isso precisa adicionar um comando no CPU pra perguntar pro LCD se já pode mandar a próxima letra.</p>
<p>De qualquer maneira eu queria passar pelos principais pontos dos videos do Ben pra que vocês pudessem ver como um CPU funciona, como ele se comunica com outros componentes como memória RAM ou um LCD simples. Como um CPU carrega um programa e executa. No fim do dia, não importa que linguagem você está usando, ele vai ser convertido em bytes de instruções parecidas como o que acabei de mostrar. Vocês viram que só de pular do binário pra escrever em Assembly já ganhamos algumas conveniências que o Assembler Vasm nos dá.</p>
<p>Quanto mais conveniências uma linguagem adiciona, mais abstrações ele faz e mais lento ele tende a ser. Como falei antes existe um trade off sempre, sempre existe um balanço entre ser conveniente e ser rápido, entre ser rápido e ser seguro e assim por diante. Em outros videos eu vou tentar explicar as diferenças das diferentes linguagens levando em consideração que vocês assistiram este vídeo.</p>
<p>No próximo video eu vou usar o que mostrei aqui de uma maneira mais prática. Eu mostrei a idéia do Game Genie no começo do video mas não fiz nada com ele hoje, é o que vou começar mostrando na parte 2. A parte 2 vai ser bem menos tedioso que esta parte 1, isso eu garanto, porque agora podemos usar tudo que eu mostrei aqui na prática em programas de verdade.</p>
<p>Muitos programadores associam &ldquo;linguagem de máquina&rdquo; com algo difícil e inatingível que não foi feito pra eles, mas isso não é verdade. Linguagem de máquina e o hardware em si são razoavelmente simples, peças de lego. Tudo funciona em cima das mesmas peças e você vai ser um programador melhor se pelo menos tiver uma imagem mental de como seu código interage com esses componentes.</p>
<p>Uma coisa parece difícil só porque você nunca viu. É que nem um truque de mágica. Mas depois que a gente revela o truque, as coisas ficam muito mais claras. Se você não entendeu o video todo, não se preocupe, eu literalmente resumi o equivalente a umas 5 horas de aula do canal do Ben Eater aqui. Vou deixar os links pro canal dele nas descrições. Se quiserem adicionar mais a esta conversa não deixem de mandar nos comentários abaixo, se curtiram o video mandem um joinha, assinem o canal e cliquem no sininho pra não perder o próximo episódio e, como sempre, compartilhem o video pra ajudar o canal. A gente se vê na próxima, até mais.</p>
<p>Se tem um tema que eu queria fazer desde que abri o canal é apresentar algumas das fundações da computação, tanto do ponto de vista de história e da evolução, mas principalmente dos fundamentos básicos que eram vários 60 anos atrás e ainda são válidos até hoje.</p>
<p>O video de hoje vai abrir com um tema de videogames, mas vamos descer bem fundo no cérebro do Nintendinho pra criar um vocabulário que eu vou usar na Parte 2, quando de fato vou mexer num emulador de Nintendo. E esse vocabulário vai servir não só pra videos futuros meus como pra tudo que você for fazer em computação. Esta é a base, sem esta base tudo que você tentar aprender de avançado depois vai ser mais difícil.</p>
<p>Infelizmente é muito difícil empacotar estes temas de uma forma que não seja maçante. Espero ter conseguido pelo menos interessar vocês no assunto.</p>
]]></content:encoded><category>ciencia-da-computacao</category><category>retrocomputacao</category><category>tutoriais</category><category>akitando</category></item><item><title>[Akitando] #71 - Usando Git Direito | Limpando seus Commits!</title><link>https://akitaonrails.github.io/2020/02/12/akitando-71-usando-git-direito-limpando-seus-commits/</link><guid isPermaLink="true">https://akitaonrails.github.io/2020/02/12/akitando-71-usando-git-direito-limpando-seus-commits/</guid><pubDate>Wed, 12 Feb 2020 10:30:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/6OokP-NE49k"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;h2&gt;DESCRIPTION&lt;span class="hx:absolute hx:-mt-20" id="description"&gt;&lt;/span&gt;
&lt;a href="#description" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Continuando o episódio sobre Git, vamos encerrar o assunto hoje. Desta vez vai ser mais ou menos um tutorial mas nada do básico. Vamos ver o que a maioria dos tutoriais de iniciantes não cobre pra você conseguir manter um repositório limpo e bem organizado. Como manipular commits. Como reescrever o histórico.&lt;/p&gt;
&lt;p&gt;E no final quero discutir rapidamente sobre o assunto de monorepos que o Google ou Facebook usam. E finalmente explicar porque o desenvolvimento da kernel do Linux, que foi de onde originou o Git, dificilmente poderia funcionar em plataformas como GitHub.&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/6OokP-NE49k"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<h2>DESCRIPTION<span class="hx:absolute hx:-mt-20" id="description"></span>
    <a href="#description" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Continuando o episódio sobre Git, vamos encerrar o assunto hoje. Desta vez vai ser mais ou menos um tutorial mas nada do básico. Vamos ver o que a maioria dos tutoriais de iniciantes não cobre pra você conseguir manter um repositório limpo e bem organizado. Como manipular commits. Como reescrever o histórico.</p>
<p>E no final quero discutir rapidamente sobre o assunto de monorepos que o Google ou Facebook usam. E finalmente explicar porque o desenvolvimento da kernel do Linux, que foi de onde originou o Git, dificilmente poderia funcionar em plataformas como GitHub.</p>
<p>Links:</p>
<ul>
<li>Git: Squash your latests commits into one (<a href="https://www.devroom.io/2011/07/05/git-squash-your-latests-commits-into-one/"target="_blank" rel="noopener">https://www.devroom.io/2011/07/05/git-squash-your-latests-commits-into-one/</a>)</li>
<li>Git and Other Systems - Migrating to Git (<a href="https://git-scm.com/book/en/v2/Git-and-Other-Systems-Migrating-to-Git"target="_blank" rel="noopener">https://git-scm.com/book/en/v2/Git-and-Other-Systems-Migrating-to-Git</a>)</li>
<li>Effectively Using Git With Subversion (<a href="https://www.viget.com/articles/effectively-using-git-with-subversion/"target="_blank" rel="noopener">https://www.viget.com/articles/effectively-using-git-with-subversion/</a>)</li>
<li>Git LFS (<a href="https://www.atlassian.com/git/tutorials/git-lfs#how-git-lfs-works"target="_blank" rel="noopener">https://www.atlassian.com/git/tutorials/git-lfs#how-git-lfs-works</a>)</li>
<li>BFG Repo Cleaner (<a href="https://rtyley.github.io/bfg-repo-cleaner/"target="_blank" rel="noopener">https://rtyley.github.io/bfg-repo-cleaner/</a>)</li>
<li>Why Github can&rsquo;t host the Linux Kernel Community (<a href="https://blog.ffwll.ch/2017/08/github-why-cant-host-the-kernel.html"target="_blank" rel="noopener">https://blog.ffwll.ch/2017/08/github-why-cant-host-the-kernel.html</a>)</li>
<li>Why don’t you have a monorepo? (<a href="https://hub.packtpub.com/why-dont-you-have-a-monorepo/"target="_blank" rel="noopener">https://hub.packtpub.com/why-dont-you-have-a-monorepo/</a>)</li>
<li>Monolithic Repos Are Evil (<a href="https://www.yegor256.com/2018/09/05/monolithic-repositories.html"target="_blank" rel="noopener">https://www.yegor256.com/2018/09/05/monolithic-repositories.html</a>)</li>
<li>Monorepos: Please don’t! (<a href="https://medium.com/@mattklein123/monorepos-please-dont-e9a279be011b"target="_blank" rel="noopener">https://medium.com/@mattklein123/monorepos-please-dont-e9a279be011b</a>)</li>
</ul>
<h2>SCRIPT<span class="hx:absolute hx:-mt-20" id="script"></span>
    <a href="#script" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Olá pessoal, Fabio Akita</p>
<p>Fiquei bem contente que vocês gostaram do vídeo de Git da semana passada. Como eu tinha dito o objetivo não era fazer um tutorial e sim dar contexto de como as coisas começaram e porque usamos Git hoje em dia pra tudo. Porém, vocês devem ter notado que o vídeo ficou bem longo passando de 1h, então obviamente eu não consegui dizer tudo que eu queria.</p>
<p>Mas como parece que vocês gostaram do assunto, resolvi fazer este logo na sequência pra encerrar. Neste episódio eu vou de fato fazer um formato mini tutorial no começo. Só que não vai ser os comandos básicos. Como eu já disse, a essa altura você já devia ter ido no site oficial do Git e lido a Documentação Oficial que faz um excelente trabalho de explicar não só o básico, mas tem bastante coisa avançada pra estudar também.</p>
<p>Na segunda metade - que começa neste tempo aqui embaixo se você quiser pular direto - eu quero dar mais contexto. Em particular rapidamente explicar porque apesar do desenvolvimento de um dos projetos open source mais importantes de todos os tempos, a kernel do Linux, ter sido a origem do Git, ninguém por lá tem muita intenção de migrar pra plataformas como GitHub. E também dar meus dois centavos sobre um assunto que talvez você tenha ouvido falar, chamado monorepos.</p>
<p>(&hellip;)</p>
<p>Pra começar eu quero explicar algumas coisas que eu acho que é importante qualquer desenvolvedor saber hoje em dia. A principal função de um repositório Git é gravar um registro preciso da história. Em qualquer momento você pode voltar no tempo e ver como seu código era exatamente uma hora atrás, ou um dia atrás, ou um ano atrás. E ele consegue fazer isso garantindo que os dados colocados lá estão íntegros, ou seja, você nunca vai puxar um dado corrompido sem o Git te avisar. E pra guardar tanta história, ao mesmo tempo ele faz o possível pra economizar espaço.</p>
<p>Apesar do Git sozinho fazer bastante coisa sem você saber, ainda assim tem maneiras de você estragar seu repositório, então o tema do mini tutorial dessa primeira metade do vídeo é um pouco sobre como zelar um pouco mais pelo seu repositório.</p>
<p>E eu acho que a primeira coisa que você deveria tentar fazer é: não escreve porcaria nas mensagens de commit. Tá ligado, mensagens do tipo &ldquo;correção&rdquo; ou &ldquo;fixing&rdquo; ou &ldquo;hotfix&rdquo; ou &ldquo;atualizando&rdquo; ou qualquer lixo assim. Sendo mais exato, você pode fazer mensagens porcaria de commit. Mas no seu próprio branch. Antes de mergear seu trabalho na master, por favor, escreve que nem gente. Vai, o tico e o teco não vão reclamar tanto assim.</p>
<p>Mas tudo bem, eu entendo, você começou a aprender Git semana passada, só sabe os primeiros comandos que todos os dez mil tutoriais por aí já repetiram milhares de vezes. Digamos que você fez um git commit -m e escreveu uma palavra errada. Mas agora já era, tá comitado. Já foi, deixa pra lá.
NÃO, sempre conserte as coisas no minuto que notou o erro. E corrigir uma mensagem de commit é super fácil, nenhum inconveniente.</p>
<p>== tutorial - begin
Vamos lá, primeiro vamos adicionar alguns arquivos no stage.
Agora vamos fazer git commit com um erro na mensagem.
Puts e agora? Se eu puxar o git log, olha que feio.
Mas não tem problema, vamos fazer git commit &ndash;amend com a mensagem certa e
boom, olha só que simples. Completamente indolor, e não te custou 2 segundos.</p>
<p>Mas vamos mudar o exemplo. Digamos que você tá programando, feliz e contente. Só pra simplificar vou só criar um arquivo vazio aqui e adicionar um commit.
Só que aí você lembra, puts, esqueci que precisava fazer outra coisa. Então vou criar outro arquivo aqui e comitar com a feliz mensagem de &ldquo;fixing&rdquo;.
Mas como sempre, nunca acaba quando você pensa, daí dá algum outro pau e você lembra que precisava adicionar outro arquivo, então vamos fazer isso e comitar com a criativa mensagem de &ldquo;fixing&rdquo; de novo.
E você pode continuar fazendo isso e de repente você achava que ia terminar meia dúzia de commits atrás e em vez disso ficou com um monte de commitzinhos com mensagens toscas de fixing.</p>
<p>== tutorial - end</p>
<p>Tudo bem, não tem nenhum problema fazer isso. Porque se você está desenvolvendo no seu branch e ainda não deu push pro servidor, você ainda tem uma chance de não passar vergonha. Tem duas formas de fazer isso. Uma delas é a forma que eu normalmente faço porque eu sou preguiçoso pra relembrar muitos comandos. O git tem um comando que faz diversas coisas, esse é o reset. O último commit que você tá trabalhando normalmente é etiquetado como HEAD ou literalmente cabeça. Eu já não falei que o Git é representado por um grafo? Mas no caso mais simples você tá lidando com uma simples lista ligada de commits e o último se chama HEAD. A importância disso é que podemos navegar por essa lista ligada fazendo HEAD (til) 3 pra apontar 3 commits pra trás por exemplo.</p>
<p>O comando git reset tem duas opções que eu uso mais. &ndash;hard e &ndash;reset. O &ndash;hard normalmente eu uso quando baguncei o código e quero simplesmente apagar tudo que eu fiz e voltar como era antes de eu mexer. É uma opção de tipo “undo” sem recuperação, então cuidado. Por outro lado eu posso fazer &ndash;soft que vai remover os commits da tal lista ligada mas não vai jogar fora as modificações desses commits.</p>
<p>=== tutorial - ini</p>
<p>É isso que eu quero, eu quero apagar os commits fixing mas não quero perder o que eu fiz. Então vamos fazer isso. git reset &ndash;soft HEAD~2 que foram os dois commits de fixing.</p>
<p>Pronto, matei os commits e agora posso fazer um novo git commit com uma mensagem decente. Se eu errar a mensagem, posso fazer &ndash;amend como falei antes. Mas, esse não é o jeito considerado oficial. Tem outro jeito. Então vamos refazer os commits feios de fixing de novo &hellip; (tempo) &hellip; pronto.
Agora podemos usar git rebase -i que significa interativo porque o git vai ficar me perguntando o que ele quer que eu faça. Se eu passar git rebase -i HEAD~2 ele vai abrir a seguinte tela.</p>
<p>Olha que fácil, ele lista os últimos commits, normalmente eu quero manter o primeiro como pick e os seguintes eu posso mudar de pick pra squash. Squash é o que você pensou mesmo, amassar tudo junto. Não precisa ser todos, eu posso inclusive pular um no meio se não fizer sentido mesclar tudo junto.
Embaixo ele te ajuda ainda dizendo quais opções você tem. Eu recomendo brincar com todas as opções num repositório local seu.</p>
<p>Vamos mudar tudo pra squash. Esse editor que ele abriu pra mim é o vim que é o que eu prefiro mas você pode mudar qual editor quer usar com a variável de ambiente EDITOR ou usando o comando git config. De novo, veja a documentação oficial do git config pra saber mais. Mas enfim, quando eu gravar e sair, ele vai abrir o editor de novo, mostrando embaixo o que esse novo commit vai conter e me pedindo pra eu editar uma mensagem decente. Então vamos tirar a mensagem padrão que ele montou e digitar uma mensagem decente.
Pronto, agora se dermos um git log, veja que bonito que ficou.</p>
<p>=== tutorial - end</p>
<p>Pro próximo exemplo, uma coisa muito comum é você começar a trabalhar numa coisa, aí você dá uma divagada, resolve codar outra coisa não relacionada, só que agora você ficou com duas coisas não relacionadas que precisa comitar. Vamos pro caso mais simples. Onde você criou arquivos relacionados a uma coisa e criou outros arquivos que são de outra coisa diferente.</p>
<p>A maioria das pessoas faz um git add . (ponto) e comita tudo junto. Tá errado! Commits devem ter só uma &ldquo;coisa&rdquo; de cada vez. Um commit pra uma pequena funcionalidade. Um commit pra correção de bugs. Um commit pra documentação. Meio difícil definir o que é essa &ldquo;coisa&rdquo; mas usa o bom senso, pra variar. Não faz sentido você misturar um arquivo CSS de uma tela com uma linha nova de README descrevendo infra, por exemplo.</p>
<p>=== tutorial - ini</p>
<p>Vamos criar uns arquivos css qualquer. E vamos criar um arquivo README por exemplo. E vamos criar mais algumas coisa só pra ficar mais realista.
A coisa mais simples é dar git add e o nome do css e fazer o commit só do css.
Depois fazer git add separado do readme e um commit só pra ele.
Quando você tem poucos arquivos esse é o jeito mais simples. Mas normalmente você se empolga, ou tá usando algum gerador de código de algum framework e fica com um tanto de arquivo.</p>
<p>Eu sei, é um saco digitar git add pra caaaada arquivo pra organizar o commit. Digamos que você começou fazendo a besteira de fazer git add . (ponto) agora todos os arquivos estão misturados no stage e se você der git commit vai tudo pro mesmo commit.
Ainda tem como consertar. Se for só um arquivo que você quer tirar do stage basta fazer git reset &ndash; (traço traço duas vezes) e o nome do arquivo. Pronto, se der git status, veja que ele saiu do stage.</p>
<p>Mas como eu falei, faz de conta que são uma dúzia de arquivos e você deu git add . de tudo. Novamente, a preguiça reina e você tá quase ligando o foda-se e fazendo commit de tudo. Calma! Ainda dá pra consertar fácil. Digite git add -i que é de novo opção interativa.
O git vai te abrir esse menuzinho e você pode usar a opção 3 que é revert. Ele vai listar os arquivos que estão no stage e você pode só digitar o número dos arquivos que não quer comitar agora.
No final dá enter pra voltar pro menu anterior.</p>
<p>Digamos que você olha e fala, puuuts, tinha um arquivo que eu tirei que era pra estar lá mesmo na real. Então você tem a opção 2 de update.
É o oposto, agora você digita o número do arquivo que quer colocar no stage.
Dá enter no final, e no menu principal digita q pra dar quit e sair.
Pronto. Vamos dar git status e olha só, é como se tivéssemos dado git add manualmente arquivo por arquivo.
Agora sim podemos fazer o commit correto e fazer git add -i de novo pra ir adicionando arquivos pro próximo commit. Super simples.</p>
<p>Mas, como eu falei, esse é o caso simples. E se no mesmo arquivo temos modificações que um é pra ir num commit e outro é pra ir só em outro commit? Vamos fazer um arquivo README qualquer com um conteúdo aleatório pra servir de base. (&hellip;)
Pronto. Agora vamos fazer 3 modificações.
Agora eu queria que  a primeira modificação e a terceira fossem um commit só. E a segunda modificação fosse um commit separado. Vamos digitar git diff pra ver o patch completo.
Olha só, se eu der git add e comitar vai tudo no mesmo commit. E agora?</p>
<p>Não se preocupem, pra isso tem git add -p. A idéia é que o git vai perguntar um hunk de modificação de cada vez.
Veja só, como as modificações estão próximas ele me pergunta se quer adicionar elas juntas pro stage. Se eu quisesse bastaria digitar y de yes, ou se eu quiser pular eu digito n pra não e esse tanto de opção aqui embaixo se eu der enter ou interrogação, ele vai explicar todas as opções que eu tenho.
Vale a pena explorar cada uma delas depois, num repositório local. Mas no meu caso eu não quero adicionar tudo junto, quero dividir. Pra isso eu digito s de split, e veja só ele dividiu o hunk mais uma vez e me deu só a primeira parte! E é isso que eu quero. Então vamos dar yes. O próximo hunk é o que eu quero separado, então digito “no” pra pular, e a última eu também quero no mesmo commit, então dou yes. Se eu quisesse parar no meio, só digitar q de quit.</p>
<p>Se eu der git status vemos que parte do arquivo README está no stage e parte não está.
Se eu der git diff &ndash;staged pra ver só o que vai no próximo commit, veja que está só o hunk que eu escolhi.
Vamos criar o commit. Pronto, e agora só sobrou a segunda modificação que eu posso adicionar no stage e criar o segundo commit. (&hellip;) Entenderam?
Essa parte pode ser meio complicada da primeira vez então recomendo assistir de novo. Mas resumindo, eu tinha modificações num mesmo arquivo que eu queria separar em commits diferentes. Pra isso usamos git add -p. Aliás esse p é de patch.</p>
<p>== tutorial - end</p>
<p>Trabalhar com o git tem sempre 3 estágios. O primeiro estágio acontece onde chamamos de working tree que é o diretório normal de trabalho com seus arquivos pra você editar e tudo mais. Pra criar um commit você precisa ir estacionando as modificações num lugar intermediário, esse lugar chamamos de stage. O Git não rastreia arquivos, ele rastreia conteúdo. Hunks e patches, lembra que eu falei no episódio passado? Podemos adicionar todas as modificações de um ou mais arquivos com os comandos git add ou git add interativo -i ou adicionar um hunk de cada arquivo de cada vez usando git add -p.</p>
<p>Aliás, como o Git rastreia conteúdo e não diretórios, por isso que diretórios vazios nunca entram num repositório git e por isso muitos frameworks reservam diretórios vazios colocando um arquivo vazio escondido como .gitkeep só pro diretório ter alguma coisa e com isso conseguir adicionar no Git. Uma vez que eu tenho tudo que eu quero no stage, posso empacotar com o comando git commit, onde você cadastra uma mensagem e o git vai terminar adicionando o resto dos metadados pra grudar seu commit como a nova cabeça do grafo, com a etiqueta de HEAD.</p>
<p>Mas mesmo depois que você já comitou, ainda dá pra voltar atrás. Você pode fazer git reset tanto hard pra apagar tudo sem dó nem piedade. Ou git reset soft pra tirar os commits do grafo e jogar as modificações de volta pro stage. Vamos fazer isso pra você ver.</p>
<p>=== tutorial - ini</p>
<p>Digamos que temos alguns commits.
Vamos fazer git reset &ndash;soft HEAD~2 pra apagar 2 commits. Essa sintaxe de HEAD e tilzinho pode ser mais complicado se você tiver merges no meio do caminho, então recomendo pesquisar e estudar como isso funciona na documentação oficial, mas pra maioria dos casos serve associar que esse número é o número de commits pra trás.
=== tutorial - end</p>
<p>Por exemplo, se você quiser reverter um commit de merge que aponta pra outros 10 commits, ele é virar um galho paralelo no histórico. Mas basta usar HEAD~1 que ele vai matar só o commit do merge que é quem liga os outros commits e você some com os 10. Isso não é tão intuitivo então é melhor você praticar.</p>
<p>=== tutorial - ini</p>
<p>De qualquer forma, demos reset soft. Se dermos git log veja que os commits desapareceram, mas você não perdeu nada.
Se der git status veja que está tudo no stage, como se você tivesse acabado de dar git add. Nesse ponto, como já fizemos antes, podemos dar git commit e empacotar tudo num commit. Mas e se alguns desses arquivos eu não quero no próximo commit?
Pra isso podemos fazer git reset &ndash; (traço traço) e o nome do arquivo. Ou só git reset &ndash; e ele vai tirar tudo do stage. Olha no git status como mudou e eles saíram do stage. A partir desse ponto podemos fazer git add -i ou git add -p como já expliquei antes.</p>
<p>Mas digamos que tô estressado e quero só marretar tudo pro lixo. Então eu vou fazer git reset &ndash;hard porque eu sei que só tem lixo aqui mesmo &hellip; e puta que pariu! tinha um arquivo que eu não podia perder!! e agora???</p>
<p>Calma!! Se você tiver o SHA-1 do último commit tá tudo certo. E por sorte demos um git log aqui em cima, então temos o SHA-1 e podemos fazer o equivalente a um undelete. Mesmo se eu não tivesse ainda ia dar, só ia dar um pouquinho mais de trabalho pra achar o commit certo. Como eu disse antes o Git é um banco de dados de grafos. Quando demos o reset só mudamos a etiqueta, ou referência do branch master dois commits pra trás. Os commits em si não foram apagados, eles foram só marcados pra serem apagados. Aliás, pro tipo: sempre evite apagar dados, melhor sempre marcar como apagado. De temos em tempos o Git roda um garbage collector, que você pode executar manualmente com o comando git gc, que seria o equivalente a esvaziar a lixeira no Windows.</p>
<p>Mas não queremos rodar o GC agora. Vamos primeiro tirar nossos commits da lixeira. Pra isso podemos rodar o comando git reflog -b (pra criar um novo branch) teste xxxxx (com o SHA-1). Aproveitando, note que normalmente usamos só um pedaço do começo do SHA-1 em vez do SHA-1 inteiro porque como raramente eles colidem, só a versão curta é suficiente pra achar o commit.
E pronto! Se dermos git log veja que o master continua apontando 2 commits pra trás, mas acabamos de anexar a etiqueta de teste pro commit antigo.</p>
<p>Se eu quiser recuperar de volta pro master, basta dar git checkout master pra voltar pro master, e como estamos trabalhando só localmente sem dar push ou pull de servidor, podemos fazer um git rebase teste.
E se dermos git log de novo veja que o branch master está apontando pro mesmo commit que o branch teste. Se listarmos os arquivos, pronto, tá tudo recuperado! Agora podemos respirar fundo e reescrever esses commits sem perder algo importante. Ah sim, e pode apagar esse branch temporário com git branch -d teste. Pronto, tudo limpo.</p>
<p>=== tutorial - end</p>
<p>Tudo que eu mostrei até agora são comandos que você pode e deve usar no seu dia a dia pra manter o histórico dos seus commits limpos. Muita gente, especialmente iniciantes e amadores, ligam o foda-se pra isso. Eu mesmo sou culpado de já ter dado push de muito commit ruim então não posso julgar tanto. O correto é deixar limpo. Repositório limpo, código limpo, tudo isso faz parte de você se tornar um profissional. É que nem sua casa ou seu escritório. Você pode jogar lixo no chão. Pode deixar as coisas sujas. Isso diz muita coisa sobre você. Se for um espaço compartilhado, demonstra sua falta de respeito com as pessoas ao redor. E nesse caso você também não pode reclamar se achar uma casca de banana em cima do seu teclado. Se você não se importa com os outros, porque os outros tem que se importar com você?</p>
<p>E se você é um tech lead ou seja lá que posição que é responsável por mergear pull requests dos outros, é parte do seu trabalho rejeitar pull requests se eles estiverem sujos e ruim. Senão pra que você serve? Rejeite sumariamente pull requests que estão quebrados, incompletos, não tem testes, estão bagunçados com coisas que não deviam estar misturados, com commits sem mensagens decentes e descritivas. Tudo isso faz parte do protocolo de boa vizinhança.</p>
<p>“Ah mas estamos com pressa, os prazos tão apertados, foda-se se tá um pouco bagunçado ou um pouco quebrado, depois a gente arruma”. Não, depois que alguma coisa é mergeada na master, ninguém nunca mais vai arrumar. Faz direito da primeira vez. Sujeira se acumula, e ninguém nunca vai limpar. O que vai acontecer, como sempre acontece, é que lá na frente essa sujeira vai aparecer. E quando a produtividade cair, número de bugs aumentar, mais pessoas novas entrarem na equipe com todo mundo copiando os maus maus hábitos, é tarde demais. É muito difícil, se não impossível, mudar maus hábitos depois. Então enforce bons hábitos no começo.</p>
<p>Dez anos atrás tinha um excelente programador na minha equipe. E ele gostava de mentorar os outros desenvolvedores. Eu não tenho certeza mas eu acho que todo mundo podia dar push direto na master, ainda não era tão comum usar Pull Requests como fluxo de trabalho. Ele basicamente apagava o commit da master se o novo código estivesse sem testes. A regra dele era muito simples &ldquo;código sem teste não tem lugar no repositório&rdquo;. E ele tinha razão. No começo todo mundo reclama, depois sabe o que acontece? Todo o código está com cobertura de teste. E a produtividade não cai. Mais do que isso, ela aumenta, porque no futuro não acumulou débito técnico.</p>
<p>Débito técnico daria um outro episódio inteiro, mas resumindo é qualquer coisa que você sabe que devia fazer, mas acha que vai te deixar mais devagar, então você escolhe pular. Você cria uma dívida. Só que como o nome diz, uma hora você tem que pagar essa dívida. E quanto mais demora mais os juros vão aumentando. Vai ficando cada vez mais caro, mais e mais rápido. É uma escolha, e às vezes realmente compensa assumir uma pequena dívida, basta ter consciência de pagar os juros sem reclamar depois.</p>
<p>Mudando de assunto, falando em projetos dos outros, vai uma outra dica. Tô parecendo vitrola quebrada, mas na documentação no site oficial do Git tem um capítulo sobre migração de projetos pra Git. Eu mencionei que mesmo se você estiver num projeto subversion, ainda assim pode usar Git. Pra fazer isso primeiro você vai instalar o suporte a subversion do Git.</p>
<p>=== tutorial - ini</p>
<p>Vamos voltar pro Ubuntu e instalar o pacote apt install git-svn. Todas as distros tem algum pacote similar. Feito isso vamos clonar o projeto do próprio subversion, que obviamente está em subversion.</p>
<p>git svn clone &hellip;.</p>
<p>Isso vai demorar um tempão, então vai almoçar. Quando voltar você vai ter um clone do repositório em Git. A partir daqui, você pode simplesmente pode dar git remote pra adicionar um repositório vazio do Github por exemplo, e dar push pra lá. E a partir desse ponto, pode jogar fora o repositório subversion. Todo o histórico está salvo.</p>
<p>=== tutorial - end</p>
<p>Mas, digamos que você não possa fazer isso porque sei lá porque motivo a empresa exige que precisa comitar no servidor de subversion mesmo. Não tem problema. Localmente tudo que eu expliquei no tutorial até agora vai funcionar igualzinho. É um repositório Git normal. De tempos em tempos você pode fazer git svn rebase pra puxar os últimos commits do subversion. E quando você tiver terminado basta agora fazer um git svn dcommit (observe o d antes da palavra commit). Me repetindo de novo, diferente de um commit de Git que é só local commit de subversion é pra mandar pro servidor. Aqui o git svn vai pegar os commits que você trabalhou localmente no Git e criar commits de subversion.</p>
<p>A graça disso é que outros desenvolvedores de subversion, quando derem svn update, vão baixar seus commits como se você nunca tivesse usado Git. A diferença é que você vai trabalhar muito melhor que todo mundo, porque você pode fazer branches locais pra experimentar coisas e trabalhar aos poucos fazendo commits pequenos, e só no final pode fazer squash dos seus branchs e criar commits limpos pra mandar de volta pro servidor legado. Era o que eu fazia em 2007. E digamos que em vez de subversion você tá usando Mercurial, Bazaar, Perforce ou mesmo o antigo TFS. Tem opções similares pra todos eles. Olhem a documentação no site oficial.</p>
<p>Uma última dica importante que muitos iniciantes fazem errado. Digamos que você é um desenvolvedor front-end. Digamos que você receba arquivos de Photoshop dos designers. E como é de costume; vira e mexe os designers ficam mandando novas versões dos photoshops. Uma hora vocês perdem noção de qual era a versão mais nova. E ninguém chega num consenso de qual era a versão do design certo pra mexer. Pra facilitar, alguém tem a grande idéia de criar uma pasta chamada photoshop na raíz do projeto e vocês ensinam o designer a usar Git também. Daí sempre que tiver versões novas, todo mundo recebe no próximo git pull e ninguém mais se confunde. Ótima idéia ….</p>
<p>Só que isso tá errado se você não souber o que é LFS. Lembrem do que eu ensinei no episódio anterior. Ferramentas como diff, patch e o próprio Git tem algoritmos muito bons pra lidar com hunks e patches de texto. Porém, photoshop é um arquivo binário. Não existe nenhuma forma eficiente de se fazer patches de arquivos binários. Se você tentar mergear binários sem saber a estrutura interna delas, a chance de dar problemas é praticamente garantido. Portanto o Git não faz deltas de binários, ele grava o binário inteiro. Aliás, ele grava o arquivo inteiro pra cada modificação.</p>
<p>Pra entender o problema pensa assim. Digamos que o photoshop original tinha 200 Mb. Você adiciona ele no Git. O designer vai mexendo, e todo dia ele cria um novo commit. E ele faz isso por 5 dias só. Em cada commit vai os 200Mb inteiro de cada versão, porque como eu disse, não tem como tirar delta de binário. Ao final de 5 dias seu repositório tem 5 vezes 200 Mb ou 1 Gb. Agora todo novo desenvolvedor que fizer um git clone vai ter que puxar 1 Gb, sendo que o código mesmo, no mesmo repositório, são poucos megas. E normalmente você só precisaria do último photoshop do designer.</p>
<p>Se for arquivos pequenos como jpgs ou pngs tudo bem, mas qualquer coisa muito maior que isso precisa ser tratado diferente. Isso é uma das arestas que ainda não é exatamente a melhor solução, mas pra casos assim, onde você precisa armazenar grandes binários, como photoshops, videos, audio, instaladores, você tem que lembrar de fazer da seguinte maneira:</p>
<p>=== tutorial - ini</p>
<p>Em Ubuntu, primeiro você precisa instalar apt install git-lfs. Pronto, agora no diretório do projeto você inicializa com git lfs install. Finalmente você tem que dizer pro Git que tipos de arquivos você quer jogar no LFS. Pra isso você faz git lfs track &ldquo;*.psd&rdquo; por exemplo. Isso vai criar um arquivo chamado .gitattributes com essa condição.</p>
<p>Agora você pode começar a dar git add de arquivos com extensão .psd. Vamos adicionar alguns psds, dar git add, criar um commit e até aqui, tudo igualzinho com era antes. Do seu ponto de vista de desenvolvedor, não parece que mudou nada.</p>
<p>=== tutorial - end</p>
<p>O que está acontecendo agora é que no commit não vai mais anexado o arquivo binário inteiro. Em vez de jogar o binário no diretório .git/objects, que eu ensinei no episódio anterior, ele vai jogar em outro diretório, o .git/lfs. E no commit em si, em vez de ir armazenado o binário inteiro, vai só um texto indicando onde tá o arquivo. Pense como se fosse um link simbólico num Linux. Assim o commit fica super leve, sem o bináriozão pesando nele. A vantagem disso é que agora quando você fizer um novo git clone do zero, não vai mais vir todo o 1 GB com todas as versões de photoshop que o designer comitou. Vai vir só o último e você pode pedir versões antigas só se precisar.</p>
<p>A desvantagem disso é que instalar e configurar o suporte de LFS num servidor de Git seu pode ser um pouco chatinho de gerenciar. Se você estiver usando GitHub ou GitLab ou outra plataforma web, esse trabalho deve ser mais transparente. Na dúvida, sempre use git lfs se tiver arquivos binários grandes no seu projeto.</p>
<p>Porém, no caso do seu projeto ser basicamente só binários grandes e pouco ou nenhum texto, eu diria que o Git não é a melhor solução. Não só git, nenhum versionador feito pra código. Imagina uma agência de design ou uma produtora de vídeo. Ninguém usa Git num lugar desses. Se você precisa lidar com binário, o fluxo de trabalho é completamente diferente. Não tem como dois designers trabalharem ao mesmo tempo, em máquinas separadas, no mesmo arquivo de Illustrator. Não existe pull request de edição de vídeo de Premiere Pro. Não tem como fazer patches num arquivo de áudio de LogicPro. Trabalho de mídia, pela própria natureza, sempre vai ser um fluxo em cascata, onde a próxima etapa só pode ser iniciada quando a anterior já terminou. E sempre que precisa voltar passos é uma dor de cabeça mesmo.</p>
<p>Por isso mesmo, não é incomum ver servidores de arquivos em rede de alta velocidade, rede de 10 gigabit, com servidores mais parrudos, com gigabytes de RAM pra fazer buffer, e soluções bem caras como os famosos Jellyfish da LumaForge, cujos modelos mais baratos como o de 32TB móvel, começa em mais de 9 mil dólares. E 32TB é bem pouco espaço na real. O modelo servidor, que começa com 80TB não sai por menos de 30 mil dólares. Tá vendo porque um Mac Pro de 10 mil dólares não é caro pra lugares como esses?</p>
<p>Voltando ao assunto. Pra poucos binários como designs de photoshop, video em mp4 de tutorial do seu produto, manuais em PDF e coisas assim, use o git lfs. Mas, digamos que você não sabia disso, e já tem um repositório com um monte de binário, e já tá sentindo que tá lento, todo mundo que faz git clone fica esperando um tempão baixar tudo pela internet. E na cidade que você mora sequer tem suporte a internet de fibra com mais banda. E agora?</p>
<p>O problema de um repositório de versionamento é que por princípio, o binário agora está anexado num commit e esse binário é usado também pra gerar o SHA-1 que identifica o commit. Lembra? Se eu tirar o binário vai necessariamente mudar o SHA-1. Qual o problema disso? Lembra que o Git é um DAG, ou seja um grafo, onde os nós são cada commit apontando pro commit anterior? E como um commit aponta pro anterior? Fácil, armazenando nele o SHA-1 desse commit anterior. Ou seja, se o SHA-1 do commit anterior mudar, o SHA-1 de Todos os outros commits que apontam pra ele mudam também.</p>
<p>Portanto, sim, é possível arrancar fora um arquivo do Git, mas pra fazer isso precisamos literalmente reescrever toda a história até o ponto onde o binário foi adicionado pela primeira vez. Se fizer isso, todo mundo que trabalhava no repositório antigo vai precisar dar um git clone novo tudo outra vez. Se você tá no ponto onde seu repositório tem gigas de tamanho e tá ficando cada vez pior, só adicionar o git lfs não vai adiantar porque o lfs vai ficar ativo só pra novos commits. Mas os antigos vão continuar no repositório.</p>
<p>Entendido isso, vamos aos finalmentes. A situação tá feia, a equipe concorda que vale a pena todo mundo parar de trabalhar um pouco pra limpar o repositório. Lembre de avisar todo mundo pra ver se deram push de tudo. Uma solução é usar o recurso de filter-branch do Git mas ele é muito lento. Não, em vez disso existe uma ferramenta feita em java chamada BFG. Eu gosto dele só pelo nome. Eu não sei se foi essa a inspiração mas qualquer um que tenha jogado Doom sabe o que é um Big Fucking Gun.</p>
<p>Vou deixar o link nas descrições abaixo. Mas resumindo, primeiro você instala Java na sua distro. Como cada um tem seu jeito, veja nas documentações pra cada sistema operacional. Num ubuntu seria um apt install openjdk. Agora você baixa o .jar neste link. Pra executar um java, se você não sabe é rodar o comando java -jar com o caminho pra onde você baixou o .jar.</p>
<p>Agora você precisa fazer um git clone &ndash;mirror do repositório gigante. Tem que ser com &ndash;mirror porque quando você acabar e der git push ele vai reescrever todo o repositório. Mais ou menos como se você fizesse um git push com force.</p>
<p>Com o BFG você pode escolher remover arquivos específicos pelo próprio nome. Mas se forem muitos, você pode escolher remover todos os blobs maiores que um certo tamanho por exemplo. Digamos que todos os seus videos tenham 100mb ou mais, então use esta opção pra remover por esse critério.</p>
<p>Tem um outro caso de uso pro BFG, se por acaso em vez de arquivos grandes na realidade você fez a besteira de iniciante que todo mundo faz, e comitou arquivos de configuração com senhas hardcoded de coisas de produção. Se você não sabia disso, deixa eu explicar: JAMAIS comite coisas com senha num repositório. Mesmo se você apagar do arquivo depois o histórico vai continuar lá. Se você fez a monumental besteira de comitar senhas no Git, agora você precisa obrigatoriamente atirar o BFG pra reescrever a história também.</p>
<p>Lembram que eu já expliquei que o git não apaga pra sempre seus commits? O BFG vai gerar novos commits e reapontar todas as referências de branchs e tudo mais corretamente, mas os commits antigos vão continuar na lixeira. Pra apagar definitivamente você vai usar o comando git gc. O procedimento completo é primeiro rodar um git reflog pra expirar tudo que você não vai precisar e depois rodar o git gc assim. (imagem)</p>
<p>Feito isso, é só dar um git push digamos pro repositório principal no Github. Agora você pode apagar essa cópia local onde rodou o BFG e todo mundo pode dar git clone de novo do novo histórico. E sim, o procedimento não é exatamente rápido nem trivial, mas também a idéia é que se você fizer certo como eu expliquei até agora, você não devia nunca precisar rodar algo assim. O BFG é pros casos raros onde você fez merda e agora temos que limpar a merda. Se isso fosse Pulp Fiction, o BFG seria Winston The Wolf e você é o Vincent ou o Jules tendo que limpar a merda dentro do carro.</p>
<p>E falando em sujeira, vamos falar rapidamente sobre monorepos. Esse é um daqueles assuntos que arrisca ser controverso, mas vou tentar simplificar pra vocês. Se você nunca ouviu falar dessa palavra, se não me engano começou com o Twitter. Mas o que interessa é o seguinte. Digamos que você é uma empresa de tecnologia gigante, mais especificamente uma com poucos produtos muito populares e centenas de desenvolvedores internos. Como num Twitter. Pior ainda, digamos que esse produto é composto por dezenas de bibliotecas e microserviços. Imagine o pesadelo logístico.</p>
<p>Você tem dezenas de pull requests aparecendo toda hora em dezenas de repositórios de código. Alguns deles podem acabar quebrando as integrações entre os diversos componentes. Num exemplo hipotético simples, digamos que num dos repositórios você atualize uma biblioteca de &hellip; parsing de JSON por conta de uma melhoria de performance. Mas digamos que em outro repositório você precise voltar uma versão pra trás dessa biblioteca porque a versão nova causa um erro de compatibilidade com seu código. E agora imagine um terceiro repositório que importa o código desses dois repositórios, cada um dependendo da mesma biblioteca em versões diferentes. Fodeu.</p>
<p>Agora imagine situações como essa acontecendo na mão de centenas de desenvolvedores, toda hora, todos os dias. Imagine a dificuldade que é no final empacotar tudo junto pra gerar um deploy coerente. Milhões de linhas de código, divididos em dezenas ou centenas de repositórios, recebendo modificações o dia inteiro pela mão de centenas de desenvolvedores, sendo vários desses times distribuídos geograficamente e separados até por fusos horários diferentes, e no final precisa empacotar uma versão de produção sem bugs. Ah, e isso tem que acontecer com bastante frequência, tipo uma vez por semana ou mesmo uma vez por dia. Ah sim, e o deploy acontece em centenas de máquinas espalhadas em dezenas de data centers pelo mundo.</p>
<p>E pra piorar imagine 20 anos de histórico de código acumulado no caso de alguém como Google. Falando em Google, no caso dele estamos falando de mais de um bilhão de linhas de código. Mais de 25 mil engenheiros. E quase 20 anos de histórico num mono repositório gigante com todo o código da empresa que hoje já passou dos 80 terabytes de tamanho. Só de código! Esse é o tipo de desafio que empresas com Twitter, Google, Facebook, Netflix, e outros gigantes enfrentam todos os dias.</p>
<p>Nesse contexto, é muito fácil de imaginar que uma das técnicas que pode ajudar é reunir todos os projetos que compõe o produto num único repositório. Mais do que isso, vendorizar, ou seja, baixar as dependências de terceiros e também comitar junto com o código; em vez de instalar bibliotecas só durante o processo de deployment em produção. Por que você acha que o Go, que é a linguagem criada pelo Google, não tem um gerenciador de dependência tradicional como um NPM de Javascript? Porque o Google prefere manter todas as dependências estáticas junto com o código do projeto. Eles também preferem compilar todas as dependências estaticamente no mesmo binariozão, de forma que não precise depender de quase nada instalado no sistema operacional. Essa é a necessidade deles, e um Go da vida foi feito primariamente pra resolver os problemas específicos deles, não necessariamente os seus.</p>
<p>Casos como Google, Twitter ou Facebook são obviamente pontos fora da curva. Você pode contar nos dedos quantas empresas tem o mesmo nível de problemas. Agora vem o ponto principal: você não é o Google! Desculpa, as chances da sua empresa ser o próximo Twitter ou o próximo Facebook é uma fração de uma fração de uma fração de um porcento, o que em matemática nós diríamos que é o limite tendendo a zero.</p>
<p>Quando um engenheiro de uma dessas empresas apresenta em palestras e eventos ou posts de blog sobre o que eles estão fazendo, esses insights servem na maior parte das vezes só pra outras empresas de porte similar. Portanto se você é uma empresa com uma equipe de 10 ou 20 pessoas, com 10 ou 20 repositórios, e só meras dezenas de milhares de linhas de código, fazendo deployment em produção uma vez por quinzena; então quase nada da escalabilidade de um Google se aplica a você. Se você é menor que isso, pode pular esse assunto por um bom tempo, ele não te interessa.</p>
<p>Uma das coisas mais prejudiciais que alguém pode fazer é adotar uma tecnologia ou uma técnica baseado na premissa errada de “se o Google usa, deve ser bom e devemos usar também”. Não, essa premissa não faz o menor sentido, porque o correto deveria ser “se o Google usa, qualquer empresa de mesmo porte e com os mesmos problemas, talvez se beneficiem e talvez valha a pena tentar”. Você tem o porte do Google? Mais importante, você tem o dinheiro que o Google investe? Lógico que não. Então não, você nunca, jamais, deve usar alguma coisa meramente porque você é fanboy de uma marca. Fanboyismo é o oposto de engenharia. Engenharia é encontrar a solução do tamanho certo pro problema certo em mãos, dentro das restrições do seu contexto. Nem mais, nem menos.</p>
<p>Agora tem um problema similar que vale a pena colocar em perspectiva. Eu mencionei no episódio anterior que o desenvolvimento da kernel do Linux não está no GitHub. Um dos motivos é que o Linus Torvalds jamais vai confiar num terceiro pra manter a cópia mestra do kernel.</p>
<p>(torvalds - google)</p>
<p>Mas tirando isso, seria bem difícil usar o fluxo de trabalho e interface gráfica ou mesmo as apis do GitHub. Assim como no caso do monorepo, daria pra ficar horas listando item a item todos os prós e contras e explicar tecnicamente os efeitos pra cada caso particular. Em vez disso quero que você leiam os links que vou deixar nas descrições abaixo e por hoje quero listar só os pontos que eu acho importantes.</p>
<p>A kernel do Linux é um dos projetos open source mais antigos em atividade hoje, e com maior quantidade de código, e com a maior quantidade de voluntários colaborando ao redor do mundo. Dezenas de empresas, de dezenas de setores, dependem do Linux. A kernel em si é tecnicamente um monorepo. Mais corretamente ela é um monotree. Diferente de projetos dentro de um Google, onde a maior parte dos repositórios é centralizada e todo mundo clona de um único lugar. No caso da kernel, existe o repositório do Linus, só que o único desenvolvedor que trabalha nesse repositório é o próprio Linus.</p>
<p>Cada distro como Ubuntu, Fedora ou outros tem seus próprios repositórios principais, que não necessariamente clonam direto do Linus. Cada fabricante de hardware que desenvolve drivers tem repositórios diferentes. Mais importante, diversos produtos estão trabalhando em versões diferentes da kernel. Por exemplo, na própria Canonical que faz o Ubuntu, você tem o Ubuntu LTS que é o Long Term Service e o Ubuntu com versões mais novas dos componentes. A idéia do LTS é que ele atualize o menos possível pra não ficar quebrando compatibilidade o tempo todo.</p>
<p>Porém, digamos que seja descoberto um problema grave de segurança num driver na kernel nova. E esse bug afeta várias versões da kernel pra trás. O bug é descoberto no Fedora primeiro. Então a correção é feita no repositório deles. O patch da correção começa a ser distribuída em mailing lists. Cada empresa ou comunidade com um repositório tem seu mailing list. A forma de distribuir pra tanta gente é literalmente usar o recurso de CC ou carbon copy de e-mail, e assim distribuir o patch pra diversos lugares diferentes.</p>
<p>Pense por um momento num GitHub. Se cada empresa tem uma organização e cada um tem um fork. É como se você tivesse um jeito de postar o mesmo pull request pra diferentes repositórios em diferentes organizações. Coisa que na interface gráfica atual, não dá pra fazer. Na interface web você só devolve pull request pro repositório de quem fez fork. Mas o problema não acaba aí. Não quer dizer que esse patch vai funcionar em todas as versões e diferentes variações de cada organização. Agora os responsáveis precisam testar, muita discussão pode acontecer. Numa mailing list essa discussão é um Reply All pra todo mundo. Num GitHub seriam as Issues em cada repositório, só que no GitHub as issues são atreladas a um repositório. Como que o cara da RedHat que tem repositório próprio vai ficar respondendo nas Issues dele e nas Issues de outras organizações como da Canonical, sobre o mesmo problema? De novo, a interface gráfica atual não tem como resolver esse problema.</p>
<p>Mais do que isso, cada área de interesse dentro do kernel, driver de rede, o X, o systemd e assim por diante tem uma lista de mantenedores. Eles que em última instância vão aprovar ou reprovar uma modificação em seu subsistema. No caso do Linux, se eu quiser achar quem é o responsável pra receber o tal patch de segurança, eu procuro no arquivo chamado MAINTAINERS que tem na raíz do projeto. No caso de um GitHub, cada mantenedor talvez teria um fork do projeto na sua conta do GitHub. Mas não existe uma forma fácil de achar quais dos forks são de mantenedores ativos e quais dos forks são de colaboradores. De novo, a interface gráfica atual não resolve esse problema.</p>
<p>Em última instância, até depois dos outros repositórios já terem aplicado o patch e deployado, a versão mestra pode acabar aplicando o patch depois dos outros, porque o Linus vai ser mais cuidadoso em aceitar os patches, talvez até rejeite e mande modificar. Mas enquanto isso a RedHat ou Canonical precisam fechar o bug pros seus clientes, independente do Linus ter aprovado o patch ou não. Vai ter muitas idas e vindas em cada patch, e não existe a obrigação de que os forks vão esperar pra dar git fetch do Linus só depois dele ter dado merge no repositório dele.</p>
<p>O fluxo de trabalho num GitHub envolve fazer forks dos repositórios, discutir Issues na interface gráfica do repositório, e receber colaborações via pull requests vindos de outros forks. O fluxo de trabalho da kernel do Linux envolve dezenas de repositórios distribuídos que são forks do repositório do Linux, com discussões acontecendo simultaneamente em dezenas de mailing lists, e esses e-mails servindo como canal de distribuição de dezenas de patches de código que são automatizados de diversas maneiras diferentes por cada empresa ou mantenedor. Uma das primeiras coisas que é diferente no fluxo é que no GitHub só tem como fazer pull request de volta pro repositório de onde se fez fork. Na kernel você quer distribuir os patches pra múltiplos repositórios de uma só vez. Além de pode discutir o assunto em paralelo entre diversos repositórios ao mesmo tempo.</p>
<p>É por essas e várias outras razões que, hoje, não tem como suportar o fluxo de trabalho da kernel no GitHub ou GitLab ou qualquer um que siga esse mesmo modelo e tenha essa escala. O fluxo deles segue uma árvore, onde o principal é a raíz e ele vai expandindo numa única direção pra cada galho. No caso do Linux é uma rede, um mesh, onde não existe direção definida, cada nó da rede pode se comunicar com qualquer outro nó da rede, sem um que coordena ou bloqueia essa comunicação. O Git foi originalmente feito com essa estrutura totalmente distribuída em mente. Hoje em dia nós simplificamos de uma forma mais centralizada, como num GitHub, pra ficar mais usável pra maioria dos mortais.</p>
<p>Em resumo, sim, se o GitHub ou GitLab resolverem criar uma estrutura de projeto de múltiplas árvores do mesmo repositório separados pra mantenedores diferentes com uma forma mais simples de unificar a cooperação entre repositórios, tanto do ponto de vista de Issues e Pull Requests distribuídos, talvez, bem talvez, um projeto como Linux poderia viver nessas plataformas. Mas seria um tanto de funcionalidade extra pra atender um nicho muito pequeno de projetos. Pouquíssimos projetos tem os requerimentos da kernel do Linux, por isso não vejo isso acontecendo tão cedo.</p>
<p>Pros colaboradores do kernel, tirando alguns subgrupos que podem trabalhar mais isoladamente; digamos, equipes de drivers ou algo assim, a grande maioria ainda vai ser mais produtiva com simples mailing lists e distribuindo patches mesmo, sem ficar na dependência de nenhuma plataforma controlando a comunicação.</p>
<p>Em ambos os casos, os monorepos do Google e Facebook ou o monotree da kernel do Linux. Nenhum é o nosso caso comum, meu ou seu. Pra maioria esmagadora dos projetos, o GitFlow do GitHub, com um repositório principal, diversos forks, e pull requests que voltam pro repositório principal, e um único lugar pra registrar Issues, ainda é o caso de uso mais produtivo.</p>
<p>Você como desenvolvedor, antes de perder muito tempo tentando achar que deveria usar monorepos porque é fã do Google ou que deveria usar fluxos de monotrees porque é fã do Linus, primeiro pára de fazer commits sujos com mensagens de “fixing” pra lá e pra cá e escreve código decente. Eu posso estar sendo injusto em generalizar, mas vou fazer isso mesmo assim: normalmente quem é fanboy demais e fica só falando em “deveríamos, deveríamos” é o primeiro que faz o trabalho mal feito, pela metade, sujo. Antes de pensar em utopias, primeiro limpa seu quadrado aí, acaba as coisas que começa, e escreve código limpo pra variar. Depois sai cagando regra por aí.</p>
<p>E por hoje é isso aí! Pelo menos pelo futuro próximo eu acho que falei o grosso do que eu tinha pra falar sobre Git. E como eu disse no primeiro episódio de Conhecimentos Gerais, Git é obrigatório saber. E Git é uma ferramenta que à medida que você vai usando e explorando, mais surpresas ele vai revelando. Não é uma coisa que você aprende num workshop de poucas horas e já acha que sabe tudo. Eu diria que se você realmente gosta de programação, Git vai te manter fascinado por um bom tempo. Comece no site oficial, leia a documentação oficial, é lá que se começa. Pare de perder tempo com divagações desnecessárias, ponha a mão na massa e escreva código limpo, pra variar. Se ficaram com dúvidas, não deixem de mandar nos comentários abaixo, se curtiram o vídeo deixem um joinha, assinem o canal e não deixem de clicar no sininho pra não perder os próximos episódios. A gente se vê, até mais.</p>
]]></content:encoded><category>controle-de-versao</category><category>tutoriais</category><category>akitando</category></item><item><title>[Akitando] #54 - O Guia DEFINITIVO de UBUNTU para Devs Iniciantes</title><link>https://akitaonrails.github.io/2019/07/12/akitando-54-o-guia-definitivo-de-ubuntu-para-devs-iniciantes/</link><guid isPermaLink="true">https://akitaonrails.github.io/2019/07/12/akitando-54-o-guia-definitivo-de-ubuntu-para-devs-iniciantes/</guid><pubDate>Fri, 12 Jul 2019 17:00:00 -0300</pubDate><description>&lt;div class="embed-container"&gt;
&lt;iframe
src="https://www.youtube.com/embed/epiyExCyb2s"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;No primeiro episódio técnico da série Começando aos 40 (&lt;a href="https://www.youtube.com/watch?v=sx4hAHhO9CY"target="_blank" rel="noopener"&gt;https://www.youtube.com/watch?v=sx4hAHhO9CY&lt;/a&gt;) eu recomendei que todos estudassem Linux, e foi só isso. Hoje eu resolvi retomar o que eu disse e mostrar que em casa de ferreiro o espeto é de ferro!&lt;/p&gt;
&lt;p&gt;Vou pegar um Ubuntu recém-instalado, e do zero ir até um ambiente completo para desenvolvedores de software (em particular Web, claro), com Docker, Tmux, Vim, ASDF e durante o percurso explicar várias coisas sobre Linux em geral, várias dicas, e até mesmo como customizar os temas pra ficar &amp;ldquo;dahura&amp;rdquo;!&lt;/p&gt;</description><content:encoded><![CDATA[

<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/epiyExCyb2s"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

<p>No primeiro episódio técnico da série Começando aos 40 (<a href="https://www.youtube.com/watch?v=sx4hAHhO9CY"target="_blank" rel="noopener">https://www.youtube.com/watch?v=sx4hAHhO9CY</a>) eu recomendei que todos estudassem Linux, e foi só isso. Hoje eu resolvi retomar o que eu disse e mostrar que em casa de ferreiro o espeto é de ferro!</p>
<p>Vou pegar um Ubuntu recém-instalado, e do zero ir até um ambiente completo para desenvolvedores de software (em particular Web, claro), com Docker, Tmux, Vim, ASDF e durante o percurso explicar várias coisas sobre Linux em geral, várias dicas, e até mesmo como customizar os temas pra ficar &ldquo;dahura&rdquo;!</p>
<p>As dicas estão espalhadas por todo o vídeo, então tente assistir o vídeo inteiro sem pular nada.</p>
<p>Até o momento esse deve ser o vídeo mais longo que eu já fiz, e eu fiz de propósito pra que seja facilmente compartilhável num link só! Compartilhe com o máximo de pessoas que puder!</p>
<p>E pra ninguém perder tempo, desta vez vou deixar listado aqui os tempos pra cada seção do vídeo pra que você possa pular direto pro que interessa caso já não seja mais tão iniciante ou se quiser relembrar alguma parte em particular:</p>
<p>== COMPLEMENTO</p>
<p>Esqueci de dizer que depois de instalar o dotfiles YADR os shortcuts de Tmux, Vim, Git mudam (vejam o código fonte!!). O Tmux em vez de Ctrl-B vira Ctrl-A por exemplo. No yadr-appearance.vim não use Solarized se quiser usar o Vim no Terminal (por alguma razão ele crasheia), troque por um &ldquo;gruvbox&rdquo; por exemplo. Pra abrir o NerdTree no terminal, use Ctrl+\</p>
<p>== ERRATA</p>
<p>Em 00:54:24 eu percebi que faltou um áudio, eu devo ter apagado por engano e só percebi agora que já subi. Era a introdução da seção de Docker e o texto era: &ldquo;Tem gente que prefere não instalar esses serviços diretamente, pelo mesmo motivo que usamos ASDF. E se você tem um projeto antigo que depende do PostgreSQL 7 e quebra se tentar conectar no Postgresql 10? Pra isso precisamos de algo parecido com um ASDF. E se não me engano acho até que o ASDF tem suporte pra Postgresql, vamos ver no google, e sim, tem mesmo. Mas se for esse o caso eu prefiro fazer diferente. Serviços como os que acabamos de instalar é o caso de uso perfeito pra usar Docker. Então é hora de instalar e configurar Docker. E a essa altura você já adivinhou: vamos pro GOOGLE.&rdquo;</p>
<p>Em 00:03:00 eu falo que o Steam roda poucos jogos em Linux mas eu realmente me esqueci do Proton que é habilitado dentro da própria interface do Steam e você pode rodar os jogos quase que normalmente em Linux também! Eu mesmo não uso porque uso Steam no Windows mas fica a dica pra quem tem insegurança de mudar pra Linux e ficar sem Steam.</p>
<p>== Seções:</p>
<p>00:03:50 - Manjaro/Arch?
00:04:19 - Que hardware/notebook devo comprar pra programar?
00:08:46 - Que máquina Akita tem? Por que não comprar um topo de linha?
00:13:27 - Ubuntu: aprendendo sobre Workspaces
00:15:40 - Ubuntu: Tema Flat-Remix
00:18:19 - Linux: Conceitos e comandos básicos
00:22:59 - Linux: Entendendo os principais diretórios do sistema
00:28:41 - Linux: Entendendo Processos e manipulando arquivos e diretórios
00:40:42 - ASDF: Instalando e gerenciando versão de TODAS as linguagens!!
00:49:30 - Bancos: Postgresql 10, Redis, Memcached e MongoDB
00:54:24 - Docker: instalando, usando Postgres em container e mais!
01:00:21 - Básico do básico de criptografia, chaves SSH
01:05:52 - Básico de TMux e Vim
01:12:28 - Dotfiles: meu preferido, o YADR
01:17:22 - Configurações finais de Desktop</p>
<p>== Links:</p>
<ul>
<li>
<p>Upgrade Your SSH Key to Ed25519 (<a href="https://medium.com/risan/upgrade-your-ssh-key-to-ed25519-c6e8d60d3c54"target="_blank" rel="noopener">https://medium.com/risan/upgrade-your-ssh-key-to-ed25519-c6e8d60d3c54</a>)</p>
</li>
<li>
<p>How to install Flat-Remix Theme on Any Linux Distribution? (<a href="https://www.osradar.com/install-flat-remix-theme-ubuntu/"target="_blank" rel="noopener">https://www.osradar.com/install-flat-remix-theme-ubuntu/</a>)</p>
</li>
<li>
<p>SWKP Dotfiles YADR (<a href="https://github.com/skwp/dotfiles"target="_blank" rel="noopener">https://github.com/skwp/dotfiles</a>)</p>
</li>
<li>
<p>VIM Cheat Sheet (<a href="https://vim.rtorr.com"target="_blank" rel="noopener">https://vim.rtorr.com</a>)</p>
</li>
<li>
<p>TMUX Cheat Sheet (<a href="https://tmuxcheatsheet.com"target="_blank" rel="noopener">https://tmuxcheatsheet.com</a>)</p>
</li>
<li>
<p>ASDF (<a href="https://github.com/asdf-vm/asdf"target="_blank" rel="noopener">https://github.com/asdf-vm/asdf</a>)</p>
</li>
<li>
<p>Understanding Shell Script&rsquo;s idiom: 2&lt;&amp;1 (<a href="https://www.brianstorti.com/understanding-shell-script-idiom-redirect/"target="_blank" rel="noopener">https://www.brianstorti.com/understanding-shell-script-idiom-redirect/</a>)</p>
</li>
<li>
<p>Upgrade Your SSH Key to Ed25519 (<a href="https://medium.com/risan/upgrade-your-ssh-key-to-ed25519-c6e8d60d3c54"target="_blank" rel="noopener">https://medium.com/risan/upgrade-your-ssh-key-to-ed25519-c6e8d60d3c54</a>)</p>
</li>
<li>
<p>Bash scripting cheatsheet (<a href="https://devhints.io/bash"target="_blank" rel="noopener">https://devhints.io/bash</a>)</p>
</li>
<li>
<p>Arguments against systemd (<a href="https://without-systemd.org/wiki/index.php/Arguments_against_systemd"target="_blank" rel="noopener">https://without-systemd.org/wiki/index.php/Arguments_against_systemd</a>)</p>
</li>
<li>
<p>Vim cheatsheet (<a href="https://devhints.io/vim"target="_blank" rel="noopener">https://devhints.io/vim</a>)</p>
</li>
<li>
<p>Tmux cheatsheet (<a href="https://devhints.io/tmux"target="_blank" rel="noopener">https://devhints.io/tmux</a>)</p>
</li>
<li>
<p>AkitaOnRails: Optimizing Linux for Slow Computers (<a href="https://www.akitaonrails.com/2017/01/17/otimizando-o-linux-para-computadores-lentos"target="_blank" rel="noopener">https://www.akitaonrails.com/2017/01/17/otimizando-o-linux-para-computadores-lentos</a>)</p>
</li>
</ul>
<p>Olá pessoal, Fabio Akita</p>
<p>Eu coloquei a série de vídeos Começando aos 40 numa playlist chamada Programação para Iniciantes, e que você pode assistir no link acima. Claro que não dá pra explicar “tudo”. Eu entro em mais detalhes em conceitos que eu sei que é muito mal explicado ou difícil de entender só lendo, como concorrência e paralelismo ou gerenciamento de memória, e meio que deixei implícito coisas que você facilmente encontra em meia hora de Google.</p>
<p>Eu imagino que pra quem está iniciando as coisas são bem menos óbvias do que pra quem já tem alguns anos na carreira. A primeira coisa que eu disse que você deveria tentar aprender é um pouco de Linux, em particular eu falei pra você tentar sofrer um pouco num Arch Linux por exemplo, e a recomendação continua valendo. Porém, e depois que você instalou o Arch ou mesmo qualquer outro Linux, como você continua e monta um ambiente de desenvolvimento suficiente pra começar a treinar programação?</p>
<p>Hoje vou seguir um pouco a tradição do meu blog, akitaonrails.com. Faz anos que mesmo quando eu estava no Mac eu não usava as coisas nativamente no ambiente UNIX do Mac, eu sempre preferi usar um Linux de verdade dentro de uma máquina virtual. Na época eu usava Vagrant que é um facilitador pra eu conseguir acessar essa máquina virtual localmente usando SSH. Apesar de parecer que todo desenvolvedor avançado usa Mac pra desenvolver, a menos que você seja um desenvolvedor de iOS pra iPhone ou iPad, você não precisa usar Macs.</p>
<p>Na verdade, o melhor ambiente de desenvolvimento sempre é um Linux. Especialmente se estamos falando de programação de aplicações Web. No final você sempre vai subir a aplicação num servidor da Amazon AWS ou Azure ou Google Cloud ou Heroku e sempre vai ser servidores Linux, por isso quanto mais perto do ambiente final de produção você puder estar, sempre vai ser melhor.</p>
<p>Mesmo num Windows, existem de fato versões de Python ou PHP e até Javascript pra Node.js que instalam e rodam nativamente em Windows. Mas não se engane, eles nunca vão funcionar 100%. Eu expliquei isso durante a série quando falei sobre como funciona compilação e dependências nativas. O comportamento é um pouco diferente e muitas bibliotecas que precisam de dependências nativas de Linux não tem similares em Windows. Na dúvida, evite as versões de Windows. Se for programar numa linguagem que se originou no mundo de código-aberto, use um sistema operacional baseado em Linux. No caso do Windows, em breve você vai poder desenvolver usando Linux usando o novo Windows Subsystems for Linux versão 2, mas mesmo ele vai precisar de uma máquina um pouco mais parruda porque vai rodar usando um hypervisor. Eu quero falar desse assunto em outro vídeo ainda.</p>
<p>Prefira instalar um Linux como sistema operacional primário e nativo na sua máquina. Na pior das hipóteses, faça um dual-boot com Windows 10 pra coisas como seus games de Steam. Sim, eu sei, Steam roda no Linux, mas a grande maioria dos principais jogos não rodam e os hacks que você precisa fazer com Wine e outras coisas pra conseguir rodar raramente compensa a menos que você seja geek de configuração como eu.</p>
<p>Isso tudo dito, o objetivo de hoje é partir de uma instalação de Ubuntu e chegar no final com um ambiente de desenvolvimento usável e demonstrar um pouco de como algumas coisas funcionam se você ainda é novato no mundo de Linux. Este episódio vai ser GIGANTE e tem múltiplas seções então pra você pode voltar depois pra seção específica que quer ver ou se quiser pular algumas partes, vou deixar os tempos de cada uma nas descrições abaixo.</p>
<p>(&hellip;)</p>
<p>Vamos lá. Eu pessoalmente prefiro usar a distro Manjaro que é uma versão de Arch mais simples de instalar. O que eu gosto em Arch é o gerenciador de pacotes Pacman e da filosofia de rolling updates, ou seja, que você não tem quebras entre versões major, tipo pular do Ubuntu 17 pro 18, ou do MacOS High Sierra pro MacOS Catalina. No caso das distros baseadas em Pacman, ele vai atualizando os pacotes continuamente, sem necessariamente dar grandes saltos que podem quebrar as coisas do nada.</p>
<p>A idéia de eu começar explicando pelo Ubuntu é porque a grande maioria das pessoas vai acabar chegando no Ubuntu primeiro e é um dos sistemas com mais suporte tanto de hardware quanto da maioria dos softwares então é o que possivelmente dê menos dor de cabeça para iniciantes. O Ubuntu 18 substitui o antigo gerenciador de janelas Unity pelo mais comum GNOME, que em termos de uso de memória é um dos mais pesados. Mas mesmo dizendo assim, hoje em dia, 300MB de RAM a mais ou a menos não é o que vai te fazer a diferença considerando que qualquer aba de Google Chrome vai consumir muito, mas muito mais que isso.</p>
<p>A atual geração de aplicativos desktop usando Javascript e navegadores deixou tudo mais pesado, principalmente em termos de RAM. O mínimo que você precisa hoje em dia é de 8GB de RAM, de preferência invista mais um pouco e consiga 16GB de memória DDR4. Um processador Core i3 é possível de usar pra desenvolver, mas você gostaria de ter um pouco mais de cores se possível e eu recomendaria tentar investir pelo menos num Core i5 acima da 6a geração Skylake.</p>
<p>Pra quem não sabe, os processadores da Intel são divididas em gerações, depois da Skylake tivemos a 7a com Kaby Lake, 8a e 9a com Coffee Lake. Todas tem processadores i3, i5 e i7 e na 9a geração temos também i9. A diferença é principalmente número de cores, os i3 são só dual-core, os i5 e i7 pode ser dual ou quad-core. Os i9 acho que tem uns 10 cores. Outra diferença, se não me engano, é o tamanho dos caches L1, L2 e L3. E também suporte a Hyperthreading. Então um Core i7 quad-core pode ter 8 threads.</p>
<p>Se você estiver com dinheiro sobrando, monte um PC torre com Core i9 9900K com 32GB de RAM e 512GB a 1TB de SSD nvme2 como um Samsung EVO. Mas pra começar você pode ir com um Core i3 9320, 8GB de RAM, 128GB de SSD normal pra instalar o sistema operacional e 512GB de HD mecânico de 5400 ou 7200 rpm se possível. Não existe a necessidade de começar com a melhor máquina, na verdade eu recomendaria começar com uma máquina fraca. Não tão fraca pra ser nível Positivo porque ali acho que nem dá pra desenvolver direito. Mas um notebook como o Dell Inspiron 15 3000 com o Core i5-8265U Skylake, 8GB de RAM, 256 GB de SSD seria um dos melhores custos benefícios no Brasil em Julho de 2019, ao custo de R$ 3.500 em até 10x. Este vídeo não é patrocinado pela Dell mas eu mesmo já usei um notebook desses e dá pra desenvolver num desses com muita sobra, pra maioria dos programadores, especialmente iniciantes. Existem opções mais baratas, mas você vai sacrificar em algum lugar.</p>
<p>Pro povo mais avançado eu sei que depois de falar tanto de Intel já já vai aparecer comentário de “mas e os AMD Ryzen”? Sim, nos anos recentes a AMD começou a voltar ao jogo das CPUs e as CPUs Ryzen são muito boas, particularmente pra multithreading porque ele vem com mais cores se comparado com equivalentes de preço da Intel. Então um AMD Ryzen 3 já vem com 4 cores comparado ao Intel Core i3 de 2 cores. O Ryzen 5 vai de 4 a 6 cores. O Ryzen 7 tem 8 cores. O Ryzen 9 tem até 16 cores e você ainda tem o Ryzen Threadripper que vai até 32 cores. Apesar de ter mais cores, a performance máxima de cada core ainda era menor se comparado aos da Intel, e em particular ele não superava a Intel em games, mas a nova geração deste ano está ultrapassando a Intel até em games!</p>
<p>Ou seja, por um preço menor você conseguiria comprar um Ryzen equivalente e seria mais barato e mais performático que a 9a geração da Intel. As CPUs da Intel tem o Turbo Boost que ativa quando você está fazendo algo particularmente intensivo de CPU, por exemplo compilando projetos gigantes em C e C++ que costuma exigir muito da CPU. Ou então em coisas como renderizar cenas 3D num Blender ou exportar vídeos no Adobe Premiere. O problema é que Turbo Boost costuma ser um pico de curta duração. Nos novos Ryzen eles tem o Precision Boost que aumenta o clock numa faixa menor que da Intel mas entrega essa velocidade maior por mais tempo e no geral pode te dar performance maior em períodos de grande carga. Ou seja, os Ryzen vieram atropelando a Intel nesses últimos 2 ou 3 anos. Se você encontrar boas configurações com Ryzen, especialmente pra nós de desenvolvimento de software, eu recomendo.</p>
<p>Muita gente que tá começando sonha com um Macbook Pro, ou um Surface Book ou algo caro assim. Eu pessoalmente tenho um Surface Book 2 e um Lenovo Extreme. Junto com o Macbook Pro são notebooks na faixa dos USD 3000 se comprar nos Estados Unidos, ou seja, máquinas que facilmente custam na faixa dos R$ 15 mil no Brasil se comprar no mercado negro. Vai por mim, você não precisa disso. Então, por que eu tenho? Sendo curto e grosso: porque eu posso. Mas faça as contas, eu tenho 42 anos, se a essa altura eu não pudesse, pelamor né.</p>
<p>Pra dar perspectiva, quando eu tinha uns 25 anos eu comprava meus notebooks via contrabandista de Miami mesmo, como todo mundo faz hoje também. Eram máquinas mais fracas, como os antigos Toshiba Sattelite em 2002. Acho que eram Pentiums com 512MB ou 1GB de RAM. Eu falei que estamos na 9a geraçao da Intel, mas a série de processadores Core da Intel só iria começar a sair a partir de 2006. SSD ainda não existia. E Java naquela época já era quase tão pesado quanto é hoje. Na verdade hoje eu sinto o Java super leve em comparação como que eu sofria em 2002.</p>
<p>Ao longo dos anos eu fui crescendo na carreira e trocando pra computadores mais potentes. Nunca se compare com alguém como eu que já tem quase 30 anos de carreira. Além disso ter um computador topo de linha não vai te beneficiar em muita coisa se você não sabe como tirar proveito de uma máquina dessas. Em janeiro de 2017 eu decidi usar um Dell Inspiron que comprei numa promoção da Magazine Luisa eu acho, um pouco melhor do que o que eu sugeri agora a pouco, mas não muito, e com HD mecânico. Eu queria ver o quanto eu conseguia otimizar o Linux pra performar nesse ambiente e escrevi um longo tutorial sobre o que é possível fazer e você pode ver nos links na descrição abaixo. E eu fiquei usando essa máquina como minha máquina primária por alguns meses sem grandes problemas.</p>
<p>Em toda profissão de prática eu acho que todo iniciante deveria começar num ambiente mais hostil. Não digo em ambientes impossíveis, mas longe do topo de linha. Um cara que consegue desenvolver bons software e ter produtividade em um Core i3, com 4GB de RAM, imagine o que ele vai conseguir fazer com um Core i9 e 32GB de RAM.</p>
<p>Por outro lado se um programador amador ruim com 32GB de RAM só vai entregar software vazando memória. Um programador que não tem interesse em otimizar e aprender mais sobre seu próprio ambiente de trabalho não faz sentido pra mim. Amanhã ou depois você vai ter um problema em produção e vai precisar tirar o máximo possível dos servidores e outros recursos que tem. Se você sequer sabe como otimizar seu próprio computador, como você espera conseguir otimizar qualquer outra coisa? Tudo faz parte do treino, todo conhecimento conta. Comece a se interessar em abrir o capô e mexer na mecânica do seu carro em vez de só dirigir e ficar de braços cruzados quando o carro não quer pegar. Nese caso você vai ser só um motorista, bons programadores dirigem e consertam o próprio carro.</p>
<p>Isso tudo dito, instalar um Ubuntu é simples, beira o trivial a menos que você tenha um hardware muito fora de padrão. Basicamente baixe a ISO do site, grave num pendrive USB usando ferramentas como o Rufus no Windows, boote pelo pendrive e siga as instruções. Primeira coisa que você precisa aprender a fazer: ler a porcaria das instruções e não só ficar apertando próximo, próximo, como minha avó faria. Metade dos erros que os programadores iniciantes travam são trivialmente resolvíveis só lendo o que aparece na tela. Leia as fucking mensagens de erro!!</p>
<p>Bom, essa parte você vai ter que seguir um tutorial como o que eu deixei linkado nas descrições abaixo. No meu caso eu estou usando Windows 10 como sistema operacional principal e por isso vou instalar o Ubuntu numa máquina virtual usando VMWare Workstation. Em outro vídeo explico porque eu uso Windows como máquina principal, mas deixando isso de lado, a instalação costuma ser muito simples. Em hardware não tão antigo (pelo menos de uns 5 anos pra cá) qualquer distro moderna de Linux deve ser capaz de identificar tudo e instalar e configurar os drivers corretos. Se por acaso você tem um hardware muito fora de padrão e não for detectado, minha sugestão é simples: ache um canto aí no seu quarto, deita em posição fetal e chora um pouco. .. chorou? Bom, agora você vai ter que ir no Google, ou direto em sites como o ArchWiki ou Stackeroverflow e sair procurando. Bom, pense que isso faz parte do aprendizado também. Eu não consigo ajudar porque não tem como eu mostrar aqui todas as variações diferentes de todos os tipos de hardware que existem.</p>
<p>(&hellip; PARTE B - SCREENCASTS)</p>
<p>Depois que terminar de instalar esta é a tela que você vai ver. O instalador já vai ter criado uma conta pro seu usuário e você pode optar por logar automaticamente nela. Você pode customizar o que quiser mas uma das mecânicas que eu recomendo que você se habitue é navegar por workspaces. Por muitos anos só desktop managers de UNIX e Linux como os antigos baseados em Motif ou os mais atuais como Cinnamon, XFCE, KDE e GNOME que é o que o Ubuntu usa hoje em dia possuem o recurso de workspaces, que é como se você tivesse mais de um monitor. Demorou algumas décadas mas finalmente hoje em dia nos MacOS e Windows você também tem isso. Mas no Ubuntu funciona assim. Vamos abrir alguns programas pra ter algumas janelas pra mostrar. Vamos abrir o Nautilus que é como o Finder no Mac ou Explorer no Windows, vamos abrir o Firefox que já vem pré-instalado, vamos abrir o Terminal apertando a tecla Super e digitando Terminal que é outro jeito de abrir programas.</p>
<p>Agora você tem a situação em que tem muitas janelas no mesmo monitor, ou no caso no mesmo workspace. Então vamos mover o Firefox pra outra workspace usando a combinação Super + Shift + Page Down e boom, agora o Firefox está na worspace de baixo. E eu posso voltar pra workspace de cima com Super + Page Up e Super + Page Down. E eu posso ficar movendo janelas entre os workspaces com Super + Shift e Page Up ou Page Down. Mais do que isso, já que estamos falando de janelas, agora que o Firefox está num workspace só dele, podemos maximizar a janela toda usando Super + seta pra cima. Ou voltando pra primeira workspace podemos maximizar na metade da esquerda usando Super pra esquerda ou Super pra direita, e assim podemos ter janelas lado a lado organizado. Treine bastante essa combinação de teclas porque costuma ser bem útil pra manter seus programas organizados. A idéia é ter conjuntos de programas relacionados no mesmo workspace. Por exemplo, você pode ter uma com Slack e Discord, outro de trabalho que tem Terminal e Sublime Text e assim por diante.</p>
<p>Mas você veio até o Linux não por causa do ambiente gráfico, senão ficaria no MacOS que ainda é o melhor ambiente gráfico que existe. Você veio por causa do que tem por baixo do capô e a porta de entrada pra isso é o Terminal. Se você nunca mexeu num Linux precisa aprender alguns conceitos muito básicos. De novo, a idéia não é ser um tutorial completo mas só mostrar o principal, isso dito, vamos abrir o Terminal de novo, maximizar e aumentar as fontes pra vocês conseguirem enxergar.</p>
<p>Aproveitando, eu sou um pouco exigente com meu ambiente e apesar de não achar o tema do Ubuntu particularmente feio, eu prefiro um tema mais elegante do que esse laranjão todo, e um dos mais usados parece ser o Flat Remix. Vou deixar o link nas descrições abaixo, mas na prática poderíamos instalar pelos pacotes de atp mas vou instalar o Git pra baixar do Github pra mostrar como instala manualmente. Então a primeira coisa vai ser <code>sudo apt install git gnome-tweak-tool fonts-hack-ttf</code> pra instalar não só o Git como o Tweak Tool do Gnome pra trocar o tema e minha fonte monospace preferida que é a Hack. Em seguida vamos criar um mkdir Projects e cd Projects pra onde podemos baixar nossos códigos.</p>
<p>Agora vamos clonar o repositório de icones do Gihub git clone <a href="https://github.com/daniruiz/flat-remix"target="_blank" rel="noopener">https://github.com/daniruiz/flat-remix</a> e em seguida vamos clonar o tema com git clone <a href="https://github.com/daniruiz/flat-remix-gtk"target="_blank" rel="noopener">https://github.com/daniruiz/flat-remix-gtk</a>. Agora vamos criar alguns diretórios com mkdir -p ~/.icons &amp;&amp; mkdir -p ~/.themes e copiar os arquivos que baixamos pra lá com cp -r flat-remix/Flat-Remix* ~/.icons/ &amp;&amp; cp -r flat-remix-gtk/Flat-Remix-GTK* ~/.themes/ Pronto, podemos abrir o Tweak Tool e mudar pro tema e os ícones para o que queremos, vamos aproveitar e trocar a fonte monospace pela Hack e mudar o wallpaper pra alguma coisa mais agradável. Viu só? Podemos trocar totalmente o look and feel e existem centenas de temas e coleções de ícones. Eu encorajo vocês a procurarem pelo visual com que mais se identificam e começar a tornar o sistema operacional mais próximo de você. Outra sugestão, se quiserem procurar é o conjunto de temas chamado Material, testem o Material Dark que também fica bacana.</p>
<p>Como eu disse antes, o instalador criou já um usuário pra você e cada novo usuário ganha um diretório próprio. Esse diretório fica em /home seguido do seu username. No caso o meu é akitaonrails e se você ficou com dúvidas de quem você é, você pode perguntar pro Linux com o comando <code>whoami</code>. A navegação por diretórios é usando o comando <code>cd</code>. Todo diretório tem 2 entradas padrão, o “.” que representa onde você está agora e o “..” que é o diretório anterior. Pra saber quais diretórios existe você usa o comando <code>ls</code> ou pra ter uma listagem mais completa faz um <code>ls -l</code> ou <code>ls -la</code> que vai te mostrar os diretórios e arquivos escondidos. O que determina o que é escondido ou não é só se o nome começa com ponto. Daí por padrão não vamos ver esses arquivos em programas como o Nautilus que falei antes.</p>
<ul>
<li></li>
</ul>
<p>Você pode criar arquivos de diversas formas, mas a mais simples pra criar um arquivo vazio é usar o comando <code>touch</code> que literalmente vai tocar o arquivo. Se já existir ele vai atualizar a data e hora de última modificação, se não existir, ele vai criar o arquivo. Aliás, pra todo comando do Linux, se você quiser rapidamente ver o que ele faz exatamente e quais opções o comando aceita, use o comando <code>man</code> que significa “manual” e não “homem”. Falando nisso, a maioria dos comandos e nomes legados de UNIX são acrônimos de poucas letras não porque a gente é masoquista, mas primeiro porque digitar comandos longos demora mais e segundo porque você precisa digitar esses comandos num terminal remoto dos anos 60. Era uma época onde você apertava uma tecla e tinha que esperar um pouquinho pra registrar no servidor e aparecer na tela, então era meio maçante ficar esperando. Se eu fosse simular seria mais ou menos assim (DELAY DIGITANDO)</p>
<ul>
<li></li>
</ul>
<p>Por isso os nomes são curtos. E pra piorar cada letra a mais que você escreve consome 1 byte (ou 2 hoje dia). Lembra quando eu contei sobre a história dos computadores eu mencionei que os grandes mainframes da época gravavam em fitas que tinham só algumas centenas ou milhares de Kbytes. Ou seja, cada letra contava. Hoje em dia não existe mais essa preocupação. De qualquer forma você precisa ir aprendendo o que significa cada comando. O comando <code>cd</code> pra mudar de diretório obviamente é change directory, o mkdir pra criar diretório é make directory, o comando <code>rm</code> pra apagar é remove, o comando <code>chown</code> é change ownership e assim por diante.</p>
<p>Você já sabe whoami pra saber “quem eu sou” no sistema, e pra saber onde estamos podemos usar o comando <code>pwd</code> que significa print working directory ou imprimir o diretório corrente. E vemos que estamos no /home/akitaonrails. Se navegarmos pra algum lugar aleatório no sistema como cd /usr/local e quisermos voltar pra nossa casa podemos fazer cd $HOME onde o cifrão denota uma variável do ambiente e você que é de Perl ou PHP deve estar mais familiarizado com essa notação. Mas cd $HOME é muito comprido, em vez disso podemos usar o atalho cd ~ til ou tilde que o shell vai expandir para o que estiver na variável $HOME e por isso chamamos isso de tilde expansion. Toda vez que você escreve ~/Documents o shell vai expandir pra /home/akitaonrails/Documents. Pra provar isso o comando echo do shell mostra a variavel, então echo $HOME mostra o diretório de casa e echo tilde também. Pra ir pro diretório de Downloads a partir de qualquer lugar basta fazer cd ~/Downloads. Se for só pra voltar pro diretório de casa nem precisa do tilde, só <code>cd</code> também funciona.</p>
<ul>
<li></li>
</ul>
<p>Quando falamos shell, na grande maioria dos casos estamos falando do famoso Bash, o Bourne Again Shell que eu mencionei no vídeo de Sua Linguagem não é Especial. Existem diversos outros shells como o tcshell e muitos desenvolvedores usam o Z shell que vou mostrar mais na frente. Mas quando executamos comandos no shell que é nossa linha de comando podemos estar executando binários do sistema, scripts com permissão de execução ou comandos diretos do Bash ou o shell que estivermos, isso pode confundir um pouco se você não souber qual dos 3 você está executando, comecem a prestar atenção nisso.</p>
<p>Voltando aos diretórios, se navegarmos pra raíz com cd / e listar o que tem lá podemos ver alguma coisas interessantes. Evita modificar qualquer coisa fora da sua home sem saber pra que serve. Bem na raíz temos só dois arquivos, esse vmlinuz e o initrd.img. Na verdade ambos são links simbólicos pra arquivos que estão no /boot. Um link simbólico é um atalho, o arquivo real mesmo está no diretório /boot como pode ver. O binário vmlinuz é o kernel do Linux propriamente dito, o primeiro binário que vai carregar logo que a máquina bootar e vai começar a carregar o sistema operacional e coisas como drivers. E o initrd.img é o segundo binário que vai carregar logo na sequência subindo um RAM disk. O vmlinuz tem só o mínimo necessário pra bootar e o resto dos módulos vem desse RAM disk carregado a partir do inird.img. É uma convenção de sistemas derivados de Debian como é o caso do Ubuntu. O motivo de ser um symbolic link ou symlink é que daí no diretório /boot podemos ter kernels de múltiplas versões e mudar de um pra outro só mudando o symlink se quisermos testar uma versão mais nova mas sem jogar fora a versão anterior.</p>
<p>Se listarmos o ls /dev podemos ver todos os dispositivos ligados nesta máquina. Alguns desses dispositivos serão coisas como drives USB, pendrives, cdroms ou até mesmo floppys. Pra gente poder gravar ou ler alguma coisa de um desses dispositivos precisamos primeiro “montar” eles com um sistema de arquivos como exFAT ou NTFS se for de Windows ou HFS+ se for de Mac ou ext4 se for de Linux e quando eles são montados normalmente criamos um subdiretório no /mnt ou /media. A maioria dos Linux modernos tem serviços pra montar automaticamente quando inserimos um USB e essas configurações ficam no arquivo /etc/fstab e podemos ver que ele tem a configuração pro caso de alguém inserir um disquete.</p>
<p>Vamos brincar um pouco. Se usarmos o comando df -h podemos listar os dispositivos que estão montados e quais são os pontos de montagem, veja o / por exemplo. Agora vamos plugar um pendrive e rodar o mesmo comando. Olha como o Ubuntu já automaticamente detectou e montou corretamente em /media/akitaonrailks/KINGSTON que era o nome do pendrive. O dispositivo de verdade está em /dev/sdb1 mas não adianta tentar executar comandos como cd ou ls nele porque não tem um sistema de arquivos ali, só no ponto de montagem. A menos que você esteja escrevendo um programa de mais baixo nível, você quase nunca vai mexer no que tem nesse /dev onde dev significa device ou dispositivo, e sim no que é montado em /media ou /mnt.</p>
<p>Seguindo, o /root é o equivalente home do usuário root que é o administrador da máquina, ainda temos o /opt onde alguns programas de fora podem ser instalados. No /sbin ficam muitos dos binários do sistema operacional. O programa shutdown fica lá. Programas pra gerenciar discos como fdisk pra reparticionar discos, o fsck pra checar o disco, o mkfs pra formatar, ifup e ifdown pra subir e derrubar placas de rede e coisas assim. No /bin fica outros programas do sistema como o próprio bash e outros shells, coisas simples como o cp pra copiar arquivos, chown e chmod pra mexer na permissão de arquivos, todos os programas pra navegar e criar diretórios como mkdir pra criar diretórios e rmdir pra remover diretórios, ou o próprio rm que apaga arquivos e vários outros são todos binários nesse diretório.</p>
<p>O diretório /usr é um dos maiores e onde fica a maior parte das aplicações que você instala. Ele significa Unix System Resources mas na prática você pode pensar nele como user ou programas dos usuários. Por exemplo, o X, os aplicativos do GNOME, o Firefox, todos esses binários ficam em /usr/bin, suas bibliotecas de dependência ficam em /usr/lib, se eles instalam código fonte junto deveria ficar em /usr/src, se você instala os pacotes -dev que trazem os arquivos de header em C ou C++ pra quando você precisa compilar dependências ficam em /usr/include e antigamente tinha o /usr/etc pra arquivos de configuração mas hoje em dia acaba ficando tudo em /etc ou em diretórios escondidos como .config no seu home pra configuração exclusiva do usuário.</p>
<p>Etc não tem um consenso de o que exatamente significa mas a maioria gosta de pensar que significa literalmente etcetera, bem coisa de programador mesmo. Finalmente temos o diretório /var contém dados variáveis, então antigamente a gente usava o /var/spool pra ser o a bobina de impressão quando a gente usava impressora, o /var/mail pra e-mails, o /var/log é onde vai todos os principais logs do sistema, o /var/run tem PIDs que são os IDs dos processos sendo executados, o /var/cache como o nome diz serve pra alguns caches do sistema.</p>
<p>Falando em processos, temos o /proc. Se você usar o comando ps consegue listar os processos dos aplicativos que estão em execução e eles tem um ID numérico. No diretório /proc esses processos são representados como diretórios e você pode fuçar ali. Por exemplo, vamos achar o PID do nosso shell Bash com <code>ps</code> e ir pro diretório dele com <code>cd /proc/aaaaaa</code> e se dermos ls podemos ver vários pseudo-arquivos que não são exatamente como um arquivo de texto que você cria mas do ponto de vista do sistema operacional, podemos operar neles como se fossem. Por exemplo, podemos usar o comando <code>less</code> pra fuçar esse arquivo chamado status ou esse arquivo chamado io pra ver um pouco das características desse processo em particular. Se você quiser entrar a fundo no mundo baixo nível, vai achar esse tipo de coisa particularmente interessante. Na prática, pra gente não tem muita coisa pra fazer aqui a não ser fuçar mesmo, mas se em algum momento você precisar fazer um programa que busca informações sobre os processos em execução, é por aqui que você vai começar. Em vez de ter alguma API complicada do sistema operacional, você pode fuçar tudo como fuçaria qualquer diretório e arquivos.</p>
<p>E falando nesses outros comandos, vamos aprender mais alguns comandos. Pra não sujar seu diretório vamos pro diretório /tmp na raíz do sistema. O /tmp é o diretório pra onde vai coisas temporárias que o sistema não garante que vão estar lá depois do próximo boot, por isso é bom pra gente jogar coisas que não nos importamos se perder. Então vamos fazer o seguinte exercício:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>cd /tmp
touch a.txt</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>serve pra tocar um arquivo como já expliquei, nesse caso vai criar um novo</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>echo &#34;hello world&#34; &gt; a.txt</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<pre><code>O echo serve pra escrever qualquer coisa no standard output, normalmente pro próprio shell mas aqui estamos conectando o standard output pra um arquivo com esse simbolo de maior. Então o hello world vai pra dentro do arquivo a.txt
</code></pre>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>cat a.txt
hello world</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<pre><code>O cat significa concatenate e serve pra concatenar arquivos mas vira e mexe usamos pra ver o conteúdo de arquivos pequenos. Evite usar cat pra arquivos grandes porque ele precisa ler tudo pra memória antes de mostrar
</code></pre>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>echo &#34;fabio akita&#34; &gt; a.txt
cat a.txt
fabio akita</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<pre><code>Se fizermos a mesma coisa com outro texto vamos ver que acabamos sobrescrevendo o que tinha antes. Então toda vez que você redireciona o standard out pra um arquivo desse jeito o conteúdo anterior morre. Mas podemos ir apendando ao fim do arquivo em vez de sobrescrever desse jeito:
</code></pre>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>echo &#34;hello world&#34; &gt;&gt; a.txt
cat a.txt
fabio akita
hello world</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<pre><code>Com o maior e maior agora podemos ir concatenando no fim do arquivo.
</code></pre>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>more a.txt
fabio akita
hello world</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<pre><code>O comando more aqui fez a mesma coisa que o cat. A diferença é quando você faz isso com arquivos maiores do que cabe na tela. Vamos dar um exemplo:
</code></pre>
<p><code>Ps aux</code>
Esse comando lista todos os processos rodando no sistema, incluindo os que não foi você que iniciou e sim o initd do sistema operacional e outros serviços. Você pode ver como ele não cabe na tela e você precisa scrolar no terminal pra ver o começo. Usando o comando more temos o seguinte:
<code>Ps aux | more</code>
Essa é uma das características marcantes dos UNIX e Linux: a capacidade de ligar o standard output de um comando ao standard in de outro comando usando o simbolo de pipe que é essa barra vertical. Eu expliquei sobre isso na minha série de Começando aos 40, por isso falei pra assistir. Mas veja como aqui ele começa a listar e pára na primeira tela e agora eu posso ir avançando aos poucos. O more é um comando antigo e primitivo, ele primeiro precisa carregar todo o conteúdo em memória pra depois ir mostrando tela a tela. Não é muito eficiente e não recomendo que faça isso com arquivos de log de gigabytes por exemplo. Na prática, você não precisa usar nem cat e nem more, existe uma versão mais moderna, o less:
<code>Ps aux | less</code>
O less faz a mesma coisa que o more, deixa você navegar por conteúdos longos que não cabem numa tela. Porém ele faz isso baseado em streams, como eu também expliquei na minha série. Em vez de carregar tudo na memória, ele vai puxando do stream só o que precisa pra preencher uma tela e navegar. Mais do que isso ele permite usar alguns dos mesmos comandos de navegação do editor de textos VI. Então você pode usar j k como cima, baixo,, você pode digitar “gg” minúsculo pra voltar pro topo do arquivo, pode digitar 10j pra descer 10 linhas ou 5k pra subir 5 linhas. E pode digitar barra com uma palavra que quer procurar e ir descendo usando n. E finalmente digitar q de quit pra parar.
<code>Ln -s a.txt b.txt</code>
Eu falei de links simbólicos ou symlinks antes e é assim que você cria: ln -s arquivo de origem e nome do symlink. Agora esse b.txt é só um atalho mas você pode usar ele como se fosse um arquivo, por exemplo se fizermos cat b.txt veremos o mesmo conteúdo de a.txt. Mas podemos fazer mais ainda.
<code>tail -f b.txt</code>
Tail é um comando pra ver o final do arquivo, você pode fazer tail -10 pra ler a 10 últimas linhas, ou pode fazer tail -f pra se pendurar no final do arquivo.
Pra demonstrar vamos abrir outro terminal e começar a apendar coisas no arquivo a.txt.
<code>echo “foo” &gt;&gt; a.txt</code>
Estão vendo como eu estou mandando coisas pro fim do arquivo a.txt mas o tail do symlink b.txt vai me mostrando o que está sendo gravado? São coisas assim que tornam o Linux um bicho diferente. Comandos como tail -f são super úteis quando você quer ficar monitorando um arquivo de log em produção por exemplo.
Eu mostrei rapidamente como podemos usar comandos como ps pra mostrar informações dos processos em execução, você pode pesquisar mais sobre os binários que estão nos diretórios /sbin ou /bin ou /usr/bin como falei antes. Mas com o conceito de pipes podemos ir mais longe e conectar o standard output de um comando ao standard input de outro comando. Por exemplo, vamos fazer
<code>Ps aux | grep bash</code>
E isso vai filtrar a saída do ps pra só mostrar as linhas que tem a palavra bash. O grep é um comando antigo que permite processar texto usando regular expressions similares de Perl 5. Hoje em dia usamos outros como o ag, que é o silversearcher. Ele aceita regex similares mas é muito mais rápido que grep. Se você não entendeu porque chama silversearcher é porque AG é o símbolo pra prata na tabela periódica. Mas digamos que estamos interessados em pegar os IDs de processo, os PIDs dos processos bash. Podemos ver que é a segunda coluna. Como filtramos por coluna? Uma das muitas opções é usar o AWK que é outra ferramenta super tradicional de UNIX e Linux e colocar outro pipe no fim desse comando:
<code>Ps aux | ag bash | awk ‘{ print $2 }’</code>
Olha o que aconteceu! Por padrão ele splita as linhas pelo separador padrão que é espaço, mas podemos configurar outros separadores incluindo regex também. O AWK é uma linguagem de programação interpretada, como um Perl, voltado a processar dados e textos.
Finalmente, digitamos vários comandos, e talvez você não se lembre de cor de tudo que digitou, ninguém lembra. Por isso shells como o Bash tem history, todos os comandos que você digitou até agora ficam gravados. Basta ir apertando a seta pra cima pra navegar pelos últimos comandos. Mas e se você quer lembrar dum comando que digitou sei lá, 2 semanas atrás? Se ficar apertando seta pra cima vai ficar fazendo isso até amanhã. Pra isso o Bash tem um atalho, digite Ctrl R e comece a digitar uma parte do comando. Agora vai apertando Ctrl R pra navegar pelo histórico filtrado pelo que acabou de digitar. Viu só?
De bônus deixa eu mostrar os últimos comandos que você precisa. Pra apagar um arquivo você faz: rm b.txt. Podemos criar um diretório com mkdir foo. Podemos mover um arquivo pra lá com mv a.txt foo. Se quisermos apagar um diretório só rm foo não funciona. Pra isso precisamos força com rm -f foo. E pronto, você já sabe o básico de como manipular diretórios, arquivos e comandos. Um comando que é complexo é o chmod e a regras de permissão mas por hoje entenda que todo binário ou script, pra ser executável precisa ter permissão de execução e a forma que você dá essa permissão é via o comando chmod +x seu_script.sh
A partir daqui é com você!</p>
<ul>
<li></li>
</ul>
<p>Veja e reveja essa demonstração que eu acabei de mostrar caso você seja um iniciante. Esses são os conceitos mais importantes que você precisa dominar pra seguir em frente. E dominar significa chegar num ponto em que você sequer pensa mais nisso. Se você usar o Nautilus que é que nem o Explorer, dá pra fazer tudo isso com o mouse. Arrastar, apertar F2 pra renomear ou DEL pra apagar um arquivo. Não tem problema. Mas quando você se conectar remotamente num servidor você não vai ter ambiente gráfico, só um shell. E por isso você precisa saber esses comandos. Mais do que isso, você precisa aprender a combinar os comandos como mostrei aqui com pipe pra fazer coisas como mover só arquivos com determinada nomenclatura pra outro diretório por exemplo, ou só arquivos que tenham determinado conteúdo.</p>
<ul>
<li></li>
</ul>
<p>E quando você precisa manipular muitos arquivos com critérios mais complicados assim, é onde programar scripts de Bash começa a fazer sentido. E sim, o Bash em si é um ambiente de programação que tem variáveis, comandos como if, e você pode executar um script que automatiza tudo pra você. E, claro, você pode combinar o Bash com outras linguagens como o AWK ou mesmo Perl, Python, Ruby ou outras linguagens e com alguns dos conceitos que mostrei pra vocês como saber que você tem seus dispositivos em /dev, seus processos representados em /proc, suas configurações em /etc e assim por diante, você pode fazer scripts que manipulam seu sistema inteiro. Aí você começa a se sentir realmente o dono da sua máquina. Tem bastante coisa pra estudar aqui.</p>
<ul>
<li></li>
</ul>
<p>Falando em outras linguagens de programação. Chegou a hora de avançarmos mais rápido e montarmos de uma vez esse ambiente. Como eu falei antes, até este ponto na instalação seu Linux está bom pra ser usado por usuários comuns. Mas pra virar sistema de desenvolvimento a primeira coisa que você precisa instalar são compiladores e todas as dependências de desenvolvimento do sistema. Pra isso vamos instalar os seguintes pacotes:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt install build-essential default-jdk libssl-dev exuberant-ctags ncurses-term ack-grep silversearcher-ag fontconfig imagemagick libmagickwand-dev software-properties-common git vim-gtk3 curl</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<ul>
<li></li>
</ul>
<p>O principal pacote é o build-essential que como o nome diz tem todo o essencial pra fazer builds. Aproveito também pra já instalar o JDK pra termos não só a JVM como o compilador do Java mais novo. Poderíamos instalar outras linguagens com o APT também como Ruby ou Python e outras, mas você precisa entender um problema desta atualidade de 2019. Linguagens mais antigas e mais estáveis como C, C++ e Java mudam com pouca frequência. De vez em quando sai patches de segurança mas não mudanças que quebram compatibilidade. Uma vez a cada X anos o C muda. Por isso é bom instalarmos via gerenciador de pacotes. Porém outras linguagens como Go ou Javascript mudam com muito mais frequência e os pacotes de distros Linux normalmente não acompanham tão de perto porque os objetivos são diferentes: as distros preferem manter versões estáveis, que já foram testadas em produção, que já receberam correções de bugs. Mas nós desenvolvedores preferimos ter acesso às versões mais instáveis justamente porque precisamos testar as coisas novas mesmo se não formos usar em produção.</p>
<ul>
<li></li>
</ul>
<p>Pior, nós trabalhamos em diversos projetos diferentes. Alguns projetos são novos e usam, por exemplo Ruby 2.6.3. Mas às vezes precisamos dar manutenção em projetos de 3 ou 4 anos atrás que podem estar ainda num Ruby 2.4.4 por exemplo. Significa que precisamos ter todas essas versões instaladas na nossa máquina ao mesmo tempo. O mesmo vale pra Javascript ou outras linguagens. Mas o gerenciador de pacotes de uma distro Linux só gerencia uma versão. Depois que ela atualiza, a versão antiga não existe mais. Então precisamos de um jeito por fora pra gerenciar isso. Ao longo dos anos cada linguagem veio construindo seu próprio gerenciador de versões como o RVM e RBENV no Ruby, ou o NVM do Javascript, ou o Virtualenv do Python. Mas também é um saco ter que ficar lembrando as sintaxes e configurações de tantas ferramentas diferentes. E pra nos salvar surgiu o gerenciador de versões Universal, o ASDF. Vamos instalar pra vocês verem.</p>
<p>(&hellip;)</p>
<p>Aliás, aprendam a usar o Google. Olhem como eu vou fazer. Eu falei ASDF, é um nominho que pode confundir o Google, coloquem github como termo se precisar. Vamos seguir o que diz na documentação pra instalar.</p>
<p>&ndash; instalar via Google</p>
<p>O truque do ASDF é usar variáveis de ambiente do shell. Quando você digitar ruby ou npm, ele não vai executar diretamente o binário chamado ruby. Na verdade ele vai procurar no PATH. Eu não falei de PATH então vamos dar echo no PATH. Tudo que você digita no shell, se não for uma instrução do próprio Bash ele entende como sendo um binário executável e vai procurar por ele nos diretórios listados nesse PATH. Você pode tentar saber de onde o Bash está executando determinado binário usando o comando <code>which</code> por exemplo o tal echo que acabamos de fazer se digitar <code>which echo</code> vemos o PATH completo dele, e aliás, o echo é um binário do sistema e não instrução de Bash.</p>
<p>Pra ficar mais fácil de gerenciar, o ASDF em si é um framework, ele não tem suporte a linguagem nenhuma. Em cima do ASDF precisamos instalar plugins. Então vamos instalar os diversos plugins pras linguagens. Leia as instruções. No caso de Javascript vemos que ele tem comandos pra instalar chaves públicas pra validar a instalação. No caso de Erlang vemos que ele manda instalar várias dependências do sistema, siga as instruções. linguagens, javascript, golang, erlang, elixir, kotlin, rust, crystal</p>
<p>Pronto, agora que instalamos os diversos plugins, podemos instalar as linguagens em si. Usamos o comando list-all do asdf pra listar quais versões estão disponívels pra instalar daí podemos fazer asdf install linguagem versão pra instalar a que queremos. Então vamos lá instalar o Ruby primeiro. Ao terminar, agora precisamos declarar que queremos usar essa versão e pra isso fazemos asdf global ruby 2.6.3 e se digitarmos ruby -v vemos que agora o Ruby está disponível. Dentro de um projeto antigo podemos usar asdf local ruby 2.4.4 depois de instalar a versão 2.4.4 pra só dentro daquele projeto ele escolher o Ruby mais antigo. E com isso resolvemos nosso problema de ter projetos que dependem de versões diferentes. Agora vamos fazer a mesma coisa pra todas as outras linguagens, javascript, golang, erlang, elixir, kotlin, rust, crystal</p>
<p>Digitem asdf help pra ver outros comandos mas os próprios plugins podem precisar de atualização e pra isso podemos fazer asdf plugins-update. E pra atualizar o asdf de vez em quando podemos fazer asdf update. Agora, se fizermos <code>which ruby</code> vemos que ele executa um script nesse diretório de reshims. Todo script executável que um ruby instalaria talvez num /usr/local/bin agora é instalado nesse reshim. Um NPM do javascript também ou outro pacote de Javascript como yarn que você instala com a flag de -g pra global. Se por acaso você instalar uma biblioteca que deveria ter um executável no PATH como webpack ou yarn mas ele não é encontrado quando você digita, pra garantir que está tudo bem, digite asdf reshim pra ele recriar esses shims, que são como proxies.</p>
<ul>
<li></li>
</ul>
<p>De qualquer forma, agora que instalamos e declaramos quais versões queremos que estejam ativas globalmente, temos acesso a todas as linguagens que quisermos. Em particular essas são as linguagens que mais me interessam hoje em dia. Mas existem dezenas de outros plugins pra tudo que é linguagem que você imaginar. Tem até coisas velhas como Prolog ou linguagens modernas como Julia. Procure e instale o plugin que você precisa e divirta-se.</p>
<p>(&hellip;)</p>
<ul>
<li></li>
</ul>
<p>Agora que instalamos todas as linguagens, vamos instalar alguns serviços que você provavelmente vai precisar. Eu sempre gosto de deixar instalado o Postgresql, o Redis e o Memcache. Um banco de dados relacional decente, um nosql que faz o mínimo que precisamos, e um cache pra quando não precisamos do Redis como banco. Tá, vou deixar instalado o Mongodb também, mas se você viu minha série de devops já sabe que eu não recomendo que você escolha Mongo como banco primário sem saber se realmente precisa. E normalmente não só você não precisa como você pode acabar usando o Mongo onde na verdade o mais adequado teria sido o Postgresql. Na dúvida, sempre escolha o Postgresql como padrão, nunca o Mongo, nem muito menos o Redis. Redis é um bom cache. E Mongo é bom pra dados que se você perder, não vai fazer muita falta, como dados de analytics ou logs.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt install postgresql-10 postgresql-contrib postgresql-server-dev-10 redis-server libhiredis-dev memcached libmemcached-dev</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Pra instalar o Mongo precisamos seguir as instruções do site oficial. Nem vou deixar o link nas descrições, vocês precisam aprender a fazer um google por mongo install ubuntu 18.04 que vai aparecer o link certo. <a href="https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/"target="_blank" rel="noopener">https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/</a></p>
<p>Ao que parece, os pacotes do Mongo não estão nos repositórios oficiais do próprio Ubuntu e isso é comum com software de terceiros. Pra começar precisamos instalar as chaves públicas do MongoDB pra validar os pacotes que vamos instalar:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora precisamos adicionar os repositórios do Mongo na lista de repositórios do gerenciador de pacotes APT e puxar a lista de pacotes:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>echo &#34;deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.0 multiverse&#34; | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list

sudo apt-get update</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Esse comando tee, é parecido com o que eu mostrei antes com o &raquo; pra apendar novas linhas no final de um arquivo. O que acabamos de fazer foi dizer pro APT procurar pacotes nesse endereço novo também.</p>
<p>Agora podemos instalar o mongo em si:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get install -y mongodb-org

sudo service mongod start</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Tanto Postgresql, Redis, Memcache e Mongo vem com daemons que podem carregar sozinhos depois do boot do sistema. Se você tem bastante memória sobrando pode deixar eles ativos que não vai fazer diferença. Se precisar derrubar ou restartar basta usar o comando service mongod stop ou restart. Porém, se você prefere que eles não carreguem automaticamente, você pode fazer service mongod disable e depois fazer service mongod start manualmente toda vez que precisar.</p>
<p>Esse comando service é importante de lembrar no Ubuntu. Ele executa esses comandos start ou stop que estão declarados no que chamamos de um init script. Esses scripts ficam em /etc/init.d por exemplo vamos ver o que tem no do redis fazendo
<code>cat /etc/init.d/redis-server</code></p>
<p>E podemos ver cada um dos comandos de start, stop, status e o que eles fazem quando são chamados. Podemos nem usar o comando service e ir direto no script também:
<code>sudo /etc/init.d/redis-server status</code></p>
<p>Mas o comando service é mais simples de lembrar. Em outros sistemas operacionais usando Systemd pra controlar daemons também existe o comando systemctl. A diferença é que com o comando service a gente faz service [nome do serviço] e [comando] tipo service mongod start. Mas o systemctl é o contrário e fazemos systemctl start mongod, eu sempre me confundo. Antigamente o Ubuntu usava o Upstart e hoje usar o Systemd. Existe uma grande controvérsia em torno desse systemd mas hoje não vou fazer essa tangente. Na prática pra você é importante só lembrar que existem serviços e daemons e é assim que você gerencia esses serviços.</p>
<p>De qualquer forma, memcache, redis e mongo, por padrão não precisam de senha pra conectar nos servidores, por isso por padrão você jamais deve expor esses servidores na internet. O Postgresql você precisa configurar usuários pra conectar no servidor e pra fazer isso você precisa se logar como o usuário postgres e de lá criar novos usuários. Se tentarmos usar o cliente de posgresql que é o psql ele vai dar erro. Então pra criar um novo usuário precisamos fazer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo su postgres
createuser --interactive - aqui crie um usuário com o mesmo nome do usuário que você está usando pra logar no sistema, isso vai facilitar sua vida
createdb akitaonrails - e crie um banco de dados com o mesmo nome do seu usuário
Exit</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<ul>
<li></li>
</ul>
<p>Pronto agora se fizermos psql provavelmente vamos logar direto no shell do postgresql. Viu? Agora quando estiver desenvolvendo sua aplicação, seu framework favorito provavelmente vai conseguir conectar fácil sem precisar declarar senha nem nada. Obviamente isso é pra ambientes de desenvolvimento. Em produção você não vai conectar num usuário que é superuser. Mas está fora do escopo deste episódio fazer tutorial de Postgresql pra produção. Mas você já sabe o que fazer: GOOGLE.</p>
<ul>
<li></li>
</ul>
<p>Tem gente que prefere não instalar esses serviços diretamente, pelo mesmo motivo que usamos ASDF. E se você tem um projeto antigo que depende do PostgreSQL 7 e quebra se tentar conectar no Postgresql 10? Pra isso precisamos de algo parecido com um ASDF. E se não me engano acho até que o ASDF tem suporte pra Postgresql, vamos ver no google, e sim, tem mesmo. Mas se for esse o caso eu prefiro fazer diferente. Serviços como os que acabamos de instalar é o caso de uso perfeito pra usar Docker. Então é hora de instalar e configurar Docker. E a essa altura você já adivinhou: vamos pro GOOGLE.</p>
<p>Seguindo as instruções, primeiro ele recomenda apagar versões antigas, caso você tenha. Como esta demonstração começou do zero, não precisamos fazer isso. Então vamos pular esse passo e instalar direto o que precisamos:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common


curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Mesma coisa que fizemos com o Mongo, instalamos chaves públicas do povo do Docker pra validar os pacotes. Se você não entendeu, todo pacote que vem nos repositórios oficiais do Ubuntu são assinados digitalmente pela chave privada do povo da Canonical que faz o Ubuntu. Mas quando adicionamos repositórios externos, eles são assinados por outras chaves. Então primeiro instalamos a chave pra depois instalar os pacotes. Esse passo é importante porque caso você seja enganado a instalar manualmente pacotes que foram modificados por um hacker, ele não vai ter a chave privada da Canonical então teoricamente o APT vai recusar instalar esse pacote. Claro, se você for enganado a instalar a chave do hacker também, aí já era, então sempre preste atenção no que está fazendo. Veja como no caso do comando anterior estamos instalando a chave vinda do domínio docker.com e não de um domínio com cara de russo.</p>
<p>Agora que instalamos as chaves, assim como fizemos com o Mongo vamos adicionar novos repositórios pro APT. A diferença é que na instrução do Mongo usamos o comando tee pra adicionar o arquivo com o repositório manualmente e aqui estamos usando o comando add-apt-repository que vem no pacote software-properties-commons.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sudo add-apt-repository \
   &#34;deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>De bônus, como eu sei que o add-apt-repository vem no software-properties-commons? Porque eu posso checar com o comando dpkg -L software-properties-commons. Viu?</p>
<p>Enfim, agora precisamos puxar a lista de pacotes novos com apt update e instalar os pacotes do Docker propriamente ditos:</p>
<p><code>sudo apt-get install docker-ce docker-ce-cli containerd.io</code></p>
<p>Você devem ter notado que eu uso o comando apt e muitas instruções vem com apt-get. Na prática apt é o jeito novo, mas apt-get ainda funciona. De qualquer forma agora temos os pacotes todos instalados. Mas o Docker ainda tem um pequena configuração: ele cria um grupo de usuários chamado docker e o usuário que vai gerenciar o Docker precisa estar nesse grupo, então vamos fazer o seguinte:</p>
<p><code>sudo usermod -aG docker $USER</code></p>
<p>Você já sabe que existe a variável $HOME que o tilde expansion usa, e o whoami que eu mostrei lá no começo usa essa variável $USER que tem o nome do seu usuário. Se quiser ver todas as variáveis do ambiente, digite o comando <code>set | less</code> pra ver as dezenas de variáveis que estão disponíveis no seu ambiente. Mas enfim, agora que nosso usuário está no grupo, acho que precisamos deslogar e logar de novo. Vamos restartar pra garantir.</p>
<p>Pra testar, basta fazer docker run hello-world e se tudo deu certo ele vai baixar a imagem de Docker de uma aplicação que só imprime um Hello. Mas nós estávamos falando em baixar o Postgresql, então vamos fazer isso. Você já sabe a rotina: GOOGLE (hackernoon)</p>
<p><code>docker pull postgres</code></p>
<p>O comando docker pull baixa a imagem mais recente. Agora vamos criar um diretório externo pra onde o postgres vai gravar os bancos de dados e tudo mais:</p>
<p><code>mkdir -p $HOME/docker/volumes/postgres</code></p>
<p>Agora vamos nos certificar que o postgresql que instalamos nativamente no sistema agora a pouco está desligado com sudo service postgresql stop e sudo service postgresql status. Agora podemos iniciar o Postgresql via Docker que acabamos de instalar:</p>
<p><code>docker run --rm   --name pg-docker -e POSTGRES_PASSWORD=docker -d -p 5432:5432 -v $HOME/docker/volumes/postgres:/var/lib/postgresql/data  postgres</code></p>
<p>Teoricamente agora podemos usar o cliente nativo psql pra conectar nesse docker assim:</p>
<p><code>psql -h localhost -U postgres -p</code>
Senha postgres</p>
<p>Pronto!! Estamos no postgresql. E se seu projeto tiver configuração pra Docker Compose, você pode subir sua aplicação web num container, seu postgresql em outro container e tudo gerenciado pelo Docker. E aproveitando que falei de Docker. Vamos dar um ps aux na máquina e você vai ver que o postgresql está visível (!!). Pra você que ainda acha que Docker é uma máquina virtual, ele não é. O postgresql está subindo normalmente, nativamente, em cima do mesmo kernel que você está. Agora vamos pegar o PID do processo e dar um cat /proc/PID/cgroup. Lembram do meu episódio sobre containers e virtualização quando expliquei que o cgroup é um jeito do sistema operacional mentir pro processo? Compare com a configuração de cgroup do bash por exemplo (ps | grep bash; cat /proc/PID/cgroup). Essa é a configuração normal pra um processo que enxerga o sistema todo e em cima vemos como é o cgroup que o Docker configura pro processo não enxergar o sistema todo. É assim que o Docker funciona e é por isso que ele é tão leve: ele está rodando lado a lado com seus outros programas.</p>
<p>Eu recomendo que você pegue qualquer tutorial pra aprender os comandos básicos. Aqui acabamos de ver o docker run e docker pull, também tem o docker ps pra mostrar os containers rodando a partir de onde podemos pegar o nome do container e o docker stop [nome do container] pra parar o postgres que acabamos de subir. Tem comandos pra gerenciar os volumes de disco virtuais, pra baixar versões diferentes desse postgres, e assim por diante. E como eu já falei, aprenda a usar Docker Compose pra criar um mini cluster de containers que conseguem acessar uns aos outros.</p>
<p>(&hellip;)</p>
<ul>
<li></li>
</ul>
<p>Já que eu mencionei chaves, vou aproveitar pra fazer uma explicação super porca. No mundo de criptografia existem pelo menos 3 tipos de tecnologias que você precisa saber. Existem algoritmos de digest, que literalmente digere qualquer blob de bits e devolve uma impressão digital de tamanho fixo. Exemplos disso são os antigos MD5 ou SHA1  e coisas mais novas como SHA512 ou Bcrypt ou HMAC. Você pode passar pra eles uma palavra ou a Wikipedia inteira e eles vão sempre cuspir um string de tamanho fixo. Isso você usa pra garantir que a informação não foi adulterada porque se você mudar um bit nessa Wikipedia que eu falei, a impressão digital vai ser completamente diferente. A gente usa isso pra tudo hoje em dia, sempre que você ver strings com caracteres que parecem aleatórios, normalmente é um fingerprint, ou impressão digital.</p>
<p>A segunda categoria são os algoritmos de encriptação reversível. Aqui é o que a maioria das pessoas pensa quando pensa em criptografia: uma forma de bagunçar a mensagem e ter uma senha. E quando passar a senha a mensagem é descriptografada. De novo você tem coisas antigas como o Triple DES ou Blowfish e coisas mais recentes como o AES que é usado pra criptografar seu HD por exemplo. Quando você vê nos filmes alguém tentando quebrar uma encriptação em força bruta ou algo assim, normalmente é um algoritmo reversível ou mais corretamente chamada de criptografia simétrica.</p>
<p>Finalmente, temos criptografia assimétrica. Nessa categoria temos a geração de duas chaves que costumam ser dois números primos relacionados. Por convenção chamamos um desses números, tanto faz qual, de chave privada e ela nunca deve sair da máquina de onde foi criada. O outro número chamamos de chave pública e ela pode ser distribuída por aí, como as chaves que instalamos do povo do Mongo e Docker antes. Tudo que for criptogrado pela chave privada só pode ser aberta pela chave pública e vice-versa. O que é criptografado com uma chave não pode ser aberta com a mesma chave, necessariamente precisamos da outra. Esse é o conceito de chaves públicas e privadas.</p>
<p>Eu expliquei tudo isso porque na nossa instalação, se você ainda nao fez isso, precisa ter seu próprio par de chaves. Serviços como GitHub ou GitLab ou tudo da Amazon AWS ou Google Cloud ou Azure, exigem que você cadastre sua chave pública na sua conta, daí o que você criptografar com sua chave privada local pode ser checada com a pública que você cadastrou e assim podemos nos certificar que você é realmente você.</p>
<p>(&hellip;)</p>
<p>Pra criar uma chave é simples, no terminal fazemos:</p>
<p><code>ssh-keygen -o -a 100 -t ed25519 -f ~/.ssh/id_ed25519 -C &quot;boss@akitaonrails.com&quot;</code></p>
<p>Normalmente a maioria dos tutoriais vai mandar você criar uma chave com algoritmo RSA mas hoje em dia  preferimos usar a ed25519 por ser mais segura. Ela vai pedir um passphrase, que de preferência como o nome diz é uma frase secreta grande que você não vai esquecer. Esse comando vai criar dois arquivos no diretório .ssh do seu home, o id_ed25519 e o id_ed25519.pub, que se você prestou atenção, representam a chave privada e a chave pública. Vamos ver o que tem dentro:</p>
<p><code>cat ~/.ssh/id_ed25519</code></p>
<p>Eu estou mostrando aqui a chave privada e não deveria obviamente, mas eu nunca vou usar essa chave de verdade, a minha está bem guardada. Mas só pra vocês verem como se parece uma chave privada. Faça backup do seu diretório .ssh que tem essas chaves. Se você perder a chave, não tem mais como recuperar só a partir da pública, obviamente. Toda vez que você precisar cadastrar sua chave, por exemplo num servidor SSH de alguém, vai precisar mandar o conteúdo do outro arquivo:</p>
<p><code>cat ~/.ssh/id_ed25519.pub</code></p>
<p>Isso é sua chave pública. Essa você pode mostrar pra qualquer pessoa. É essa chave que você cadastrar na Amazon ou no GitHub, por exemplo nessa página deles (mostra GITHUB).</p>
<p>Pra você não ter que ficar digitando a passphrase toda hora, na sua máquina precisa ter o serviço ssh-agent. Você checa se ele tá rodando assim:</p>
<p><code>eval &quot;$(ssh-agent -s)&quot;</code></p>
<p>Agora adicione sua nova chave no agente, daí ele só vai pedir sua passphrase uma vez e você pode ficar se logando nos servidores SSH que precisar sem toda hora ter que redigitar a passphrase:</p>
<p><code>ssh-add ~/.ssh/id_ed25519</code></p>
<p>Muito bem, agora você é um cidadão respeitável e criptograficamente identificável na nossa sociedade. NUNCA  mas NUNCA compartilhe sua chave privada. E faça backup num pendrive que você vai guardar num cofre ou algo assim. Enfim, cuide da sua chave, ela é sua identidade. Se por acaso perder, gere outra e substitua a chave pública em todos os servidores e serviços que tem acesso. Quem tiver sua chave privada tem acesso a tudo que você tinha acesso.</p>
<p>E já que falamos em Github, falta configurar o Git também. Existem milhares de bons tutoriais pra iniciantes no GOOGLE então aprenda de uma vez porque isso é obrigatório, mas a primeira coisa que você precisa fazer é configurar seu nome e seu email assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>git config --global user.name &#34;AkitaOnRails&#34;
git config --global user.email &#34;boss@akitaonrails.com&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<ul>
<li></li>
</ul>
<p>Isso é a configuração global padrão. Então todo commit que você fizer vai com essa identificação. E se você quiser usar nomes e e-mails diferentes pra projetos diferentes, você pode configurar no arquivo .git/config em cada projeto também, assim você pode ter e-mail pessoal nos projetos pessoais e e-mail da empresa em projetos de clientes se fizer muita questão. Eu uso sempre meu pessoal pra tudo.</p>
<ul>
<li></li>
</ul>
<p>Pronto, agora temos os principais pacotes de desenvolvimento instalados, temos todas as linguagens que vamos precisar, temos até bancos de dados e docker instalados. Agora vem uma parte que é muito particular só pra mim. Eu gosto muito de usar VIM como editor de textos. Hoje em dia você pode ser virar muito bem com Visual Studio Code que eu recomendo como uma das melhores opções modernas, seguindo do bom e velho Sublime Text 3. Tem o Atom do GitHub também mas eu acho que o Atom é muito pesado por ser totalmente em Electron e eu prefiro evitar Electron como eu evitaria uma praga. O VS Code tem partes em Javascript mas se não me engano ele usa uma toolkit híbrida com componentes nativos e isso torna ele mais responsivo. Porém tanto VS Code quanto Atom, por terem muito Javascript por baixo, é que nem uma aba de Google Chrome: come memória como se não houvesse amanhã. O Sublime Text 3 é totalmente nativo e extremamente leve. E o VIM obviamente é mais leve ainda. Eu gosto de editores de texto que eu chamo e abrem instantaneamente, em milissegundos pra ser mais exato, senão eu me entedio muito rápido.</p>
<ul>
<li></li>
</ul>
<p>Na realidade o GVIM já está instalado. O VIM é um editor que você abre na linha de comando, mas existe o VIM-GTK que é o mesmo VIM só que aberto dentro de uma janela gráfica de GNOME e com algum suporte básico a mouse também. Tanto faz qual dos dois você vai usar. Na verdade faz diferença. Aqui eu vou dividir o caso de uso pra usuários como eu que tiveram história usando tantos ambientes puramente de texto e também ambientes gráficos como Mac e aqueles que tem a oportunidade de começar sem vícios só no ambiente de texto do Linux. Agora eu vou mostrar a combinação que a maioria dos veteranos de Linux realmente gostam. Vamos instalar o TMUX, o sucessor do lendário GNU Screen que a gente usava antigamente.</p>
<p>(..)</p>
<p>Pra instalar é simples: <code>apt install tmux</code>.</p>
<p>O TMUX é um multiplexer de terminal. Esse palavrão significa que você ganha a capacidade de ter mútiplas sessões de terminal dentro de um único terminal. Lembram os workspaces gráficos do GNOME que seria como se fossem monitores virtuais? O TMUX são terminais virtuais. Você inicia com o comando tmux. Aqui ainda não tem nada demais, parece o mesmo terminal de sempre. Vamos abrir um VIM agora e editar alguma coisa. O básico de VIM é que ele tem dois modos, modo de comando e modo de edição. Ele sempre está no modo de comando, mas apertando comandos como i ou a de insert ou append, você entra no modo de edição. Agora você pode editar normalmente e quando terminar aperte ESC e volta pro modo de comando. Agora podemos gravar o arquivo com o comando :w /tmp/teste.rb por exemplo. Nesse modo também podemos navegar, por exemplo com as teclas hjkl que no teclado ficam um do lado do outro e servem como setas. Podemos fazer gg ou G pra ir pro começo ou fim do arquivo. Podemos combinar comandos como 10l pra andar 10 caracteres pra direita. E pra sair do VIM apertamos ESC pra garantir que estamos no modo de comando e fazemos :q pra dar quit. Isso é o mínimo do mínimo pra você navegar no VIM. Mas voltando ao TMUX.</p>
<p>Digamos que eu queira executar esse script idiota que eu acabei de fazer. Eu posso agora apertar a combinação Ctrl b % e boom temos um split do terminal. Com Ctrl b O eu posso navegar pelos painéis. Ctrl b “ faz split em outra direção. E Ctrl b w fecha o painel. Fala que isso não é bem foda. Tanto TMUX quanto VIM tem essa funcionalidade de split horizontal e split vertical que eu acho ultra útil e sei lá porque razão nenhum outro editor de textos tem exatamente a mesma funcionalidade. Terminais que fazem split tem vários hoje como o Tilix que vamos instalar já já, ou o iTerm2 em MacOS.</p>
<p>Mas o que torna o TMUX diferente é o seguinte: e se eu fechar o aplicativo do Terminal inteiro? Vamos fazer isso. Se fosse no Terminal normal ou num iTerm2 da vida, você acabou de perder tudo que estava fazendo e vai ter que abrir tudo. Mas não no TMUX eu posso fazer o comando tmux ls, pra listar as minhas sessões e olha só: tem uma sessão ativa. Agora posso fazer
tmux attach-session -t 0 que é o número da sessão e boom, tudo exatamente com eu tinha deixado. Isso era particularmente útil quando você só tinha terminais burros conectados remotamente num servidor via TELNET porque e se a conexão caísse bem quando você estava no meio de uma edição de código? Você perdia o que estava fazendo. Mas se estivesse usando o GNU Screen a sessão permanecia rodando no servidor, você podia esperar a conexão voltar, conectar de novo com seu terminal burro e recuperar a sessão que ainda estaria ativa.</p>
<p>Acho que isso deve convencer vocês a aprender tmux. Da próxima vez que precisar se conectar via SSH num servidor remoto pra fazer alguma coisa que vai demorar, abra um TMUX primeiro. Se por acaso der pau na sua máquina ou na sua conexão por alguma razão, você não vai perder nada que estava fazendo. Basta reconectar e dar attach na session que ainda vai estar ativa lá. E pra ser ainda mais versátil, você pode ver que já tem paineis demais aqui né? Vamos criar uma outra janela inteira com Ctrl b c e agora podemos navegar com Ctrl b n. Ou seja, você pode estar fazendo várias coisas em paralelo em múltiplas janelas de terminais splitadas em múltiplos painéis e você não vai perder nada caso feche essa janela. No final você pode fazer Ctrl b d pra desatachar manualmente. Se quiser mesmo fechar tudo que estava aberto na sessão você pode fazer tmux kill-session -t 0. Pra saber todos atalhos de teclas e comandos que o tmux aceita, o man tmux vai ajudar muito.</p>
<ul>
<li></li>
</ul>
<p>Pronto, com isso você tem o ambiente perfeito. Mas ainda falta mais uma coisa. O VIM puro é bem inútil na verdade, ele não é muito diferente de um NANO que você vê em muito tutorial. Eu particularmente fico irritado de ver o nano. O nano é derivado do antigo editor PICO que era o editor do cliente de e-mails antigo chamado PINE que eu inclusive usei muito na faculdade no meio dos anos 90. PICO era conterrâneo do editor JOE que por sua vez era inspirado no editor de MS-DOS, o Wordstar que eu usava no fim dos anos 80. São editores velhos, cansados, que eu não teria coragem de usar hoje em dia pra programar.</p>
<p>O VIM, assim como o Emacs são extremamente versáveis porque são scriptáveis. O Emacs em Emacs Lisp ou eLisp e o VIM em VimScript. Por causa disso podemos adicionar funcionalidades modernas como colorização de sintaxe pra cada linguagem, auto complete e muito mais. Infelizmente eu nunca tive muito saco pra aprender Emacs e o Vim sempre foi suficiente pra mim. Ele é leve e tem todas as funcionalidades que eu preciso. Mais do que isso: ele é perfeito pra editar coisas remotamente via SSH se precisar. Ele foi feito pra ter comandos e atalhos curtos, pra era onde conexões de rede eram lentas. E isso é perfeito porque mesmo hoje em dia ainda temos que lidar com conexões lentas ou instáveis de vez em quando.</p>
<p>E aqui vem a parte que é bem subjetivo ao mesmo próprio gosto. Por isso esta parte é opcional e você vai encontrar outras configurações diferentes. Até agora estávamos usando o Terminal que já vem no Ubuntu, eu prefiro usar o Tilix como já falei. Eu também prefiro usar o Z Shell em vez do Bash e eu tenho um conjunto de dotfiles que eu já me acostumei e gosto bastante.</p>
<p>Então vamos instalar:</p>
<p><code>sudo apt install tilix zsh</code></p>
<p>Feito isso agora é hora de instalar os dotfiles que eu gosto. Lembre dessa palavra dotfiles. Eu já expliquei que todo arquivo ou diretório que começa com ponto é escondido, e no seu diretório home vai começar a aparecer vários assim como o .config que o GNOME usa pra colocar configurações ou o .gcloud se você configurou as ferramentas do Google Cloud. Mas do que isso tem .vim pra configurações do VIM. Mas existem vários projetos no Github com dotfiles pré-prontos, um dos mais famosos pra ZSH é o Oh-my-ZSH que é bem customizável mas eu prefiro o do usuário SKWP que se instala assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>sh -c &#34;`curl -fsSL https://raw.githubusercontent.com/skwp/dotfiles/master/install.sh `&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ele precisa de Ruby, por isso já instalei via ASDF lá atrás. Vamos entrar no diretório .yadr e rodar rake update e deixar ele trabalhar.</p>
<p>Pronto, no final ele vai instalar temas como o Solarized Dark, vai configurar o prompt pra mostrar o branch de Git que você está e deixa tudo organizado no lado do ZSH e do VIM pra você customizar e adicionar novos plugins se precisar. Por exemplo, eu quero mudar a fonte do VIM pra ser a fonte Hack que instalamos antes. O VIM tem uma tecla especial chamada de Leader que dependendo do dotfiles pode estar assinalada pra qualquer coisa mas neste pacote é a vírgula. Com virgula T eu posso procurar por arquivos como o Ctrl P no Sublime ou VS Code. Vamos abrir o yadr-appearance. Olha como está tudo colorido corretamente. Vamos navegar e adicionar a fonte.</p>
<p>No Tilix também quero mudar o tema pra ser Dark e usar Solarized com fonte maior e bordas menores. E também mudar o atalho de splitar a tela que seria Ctrl Alt D para outra coisa como Ctrl Alt E e a razão disso é porque no Ubuntu esse atalho já é usado pra esconder a janela. E pronto. Veja porque eu disse que aqui é uma questão de que tipo de usuário você é. Você pode usar um terminal maximizado com TMUX e splitar pra ter o VIM de um lado e o terminal do outro pra você trabalhar, OU, no caso de alguém como eu que já se acostumou ao iTerm2 no Mac e MacVim, eu prefiro deixar um terminal aberto de um lado que eu posso facilmente splitar com Ctrl Alt E e Ctrl Alt R e navegar com ALT e numero do terminal e do outro lado o GVIM que eu também posso splitar usando ss ou vv e navegar com ctrl shit e as teclas de navegação hjkl.</p>
<p>Só pra exibir essa versão customizada de vim, veja como eu posso facilmente substituir uma palavra inteira com ciw ou usar o pacote de git pra dar um :Gblame ou :Gdiff, ou então abrir um explorador de arquivos inteiro que é o NerdTree com Alt Shift N e os comandos de navegação são os mesmos pra painéis splitados então ctrl alt l ou j. E :q fecha cada painel. :w grava. :e serve pra abrir outros arquivos. Posso usar :Ag que se integra com o silversearcher pra procurar rapidamente em todos os arquivos do projeto.</p>
<ul>
<li></li>
</ul>
<p>E agora sim, eu tenho tudo que eu preciso instalado. Se eu estiver no humor de usar 100% terminal, posso abrir o TMUX com o VIM de linha de comando, ou se eu quiser ficar no mundo híbrido gráfico posso usar os splits de Tilix com GVIM. Tanto faz, contanto que você tenha todos os atalhos e shortcuts pra ser produtivo. Na prática o jeito 100% é o melhor porque evita que você precisa ficar toda hora usando o mouse, e isso é uma das coisas que vai evitar que você tenha tendinite no futuro. Por isso quanto mais você aprender os shortcuts de TMUX e VIM, menos você vai precisar usar o mouse e isso sempre vai ser mais produtivo mesmo. Fora que as mesmas ferramentas que você usar na sua máquina de desenvolvimento, você também vai poder usar remotamente via SSH num servidor. E só de bônus, você pode compartilhar sessões de TMUX com outro usuário via SSH pra fazer pair programming remoto. Eu não vou mostrar aqui hoje mas você já sabe no GOOGLE você acha como.</p>
<p>Pra finalizar o vídeo de hoje, se você vai usar esse Linux que acabou de instalar como sua máquina primária, tem uma última coisa que você vai precisar fazer se quiser assistir vídeos online. Os codecs de H.264 ou mp3 e outros não são free software, então você precisa habilitar o que o Ubuntu chama de repositórios restritos. A forma de fazer isso é instalando o pacote ubuntu-restricted-extras pelo apt.</p>
<p>Esses repositórios são considerados restritos porque na instalação base vem tudo que é 100% código aberto. É parte da Filosofia do mundo de Software Livre que você deveria boicotar tudo que não tem código-aberto ou usa licenças que não são de software livre. E eles tem bons argumentos, porém, eu não sou ativista, eu prefiro ter o melhor dos dois mundos sempre. Minha produtividade nunca vai ser boa se eu tiver ideologias extremistas. Dou meus parabéns a quem vem lutando pra conseguir ter o máximo de código-aberto no mundo, mas no mundo real nem sempre isso é possível e eu prefiro não me restringir.</p>
<p>O Ubuntu vem por padrão com o Firefox e eu recomendo que você tente mesmo usar o Firefox como seu navegador primário, o mundo está sendo monopolizado pelo Google Chrome e mesmo navegadores como o novo Microsoft Edge, o Brave e o Opera usam a mesma engine do Chrome. Novamente, eu preferia que o mundo tivesse mais opções, mas na realidade se você é um programador web, vai precisar se certificar que sua aplicação tanto em Firefox quanto em Chrome, então você precisa instalar o próprio Chrome do Google ou a versão aberta que é o Chromium e pra instalar é via apt install chromium-browser.</p>
<p>Procure por Ubuntu 18 post-install e veja o que os outros fazem logo depois do primeiro boot. Você vai achar como instalar outros aplicativos úteis pra você como Spotify, Discord, Skype e muito mais.</p>
<p>E meu deus do céu, esse vídeo deu MUITO trabalho, mas eu precisava ter ele gravado pra poder referencias nos próximos vídeos, assim quando eu quiser mostrar a parte de programação de verdade ninguém vai poder dizer que não sabe o que é Docker, Postgres, ASDF, Vim e muito menos que não sabe como instalar, porque eu vou mandar sempre pra cá. Obviamente se você é iniciante deve ter ainda muitas dúvidas, então mandem nos comentários abaixo. Se tudo isso que eu falei você já sabia contribuem mandando mais dicas e ajudando a galera que tá começando agora nos comentários também. Se curtiram esse vídeo mandem um joinha, assinem o canal, e cliquem no sininho pra não perder os próximos episódios. Compartilhem o vídeo com seus amigos. A gente se vê semana que vem, até mais!</p>
]]></content:encoded><category>linux</category><category>ferramentas-de-desenvolvimento</category><category>tutoriais</category><category>akitando</category></item><item><title>Iniciante: Longa Vida ao PhantomJS - Vamos Usar Chrome Headless Agora</title><link>https://akitaonrails.github.io/2017/10/31/iniciante-longa-vida-ao-phantomjs-vamos-usar-chrome-headless-agora/</link><guid isPermaLink="true">https://akitaonrails.github.io/2017/10/31/iniciante-longa-vida-ao-phantomjs-vamos-usar-chrome-headless-agora/</guid><pubDate>Tue, 31 Oct 2017 19:48:00 -0200</pubDate><description>&lt;p&gt;Se você faz Feature Specs — o processo de subir um servidor real da aplicação e um browser headless real para testar funcionalidades do ponto de vista do usuário — então você conhece o &lt;a href="https://github.com/teamcapybara/capybara/issues/1860"target="_blank" rel="noopener"&gt;Capybara&lt;/a&gt; e um dos seus drivers mais famosos, o &lt;a href="https://github.com/teampoltergeist/poltergeist/issues/882"target="_blank" rel="noopener"&gt;Poltergeist&lt;/a&gt;. O Poltergeist encapsula o PhantomJS, que é um browser headless baseado em WebKit bastante conhecido.&lt;/p&gt;
&lt;p&gt;Só que o WebKit é famoso por ser &lt;strong&gt;muito&lt;/strong&gt; complicado de lidar. Então dá pra imaginar o pesadelo que é manter o PhantomJS, que é basicamente manter um browser completo como Chrome ou Safari.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Se você faz Feature Specs — o processo de subir um servidor real da aplicação e um browser headless real para testar funcionalidades do ponto de vista do usuário — então você conhece o <a href="https://github.com/teamcapybara/capybara/issues/1860"target="_blank" rel="noopener">Capybara</a> e um dos seus drivers mais famosos, o <a href="https://github.com/teampoltergeist/poltergeist/issues/882"target="_blank" rel="noopener">Poltergeist</a>. O Poltergeist encapsula o PhantomJS, que é um browser headless baseado em WebKit bastante conhecido.</p>
<p>Só que o WebKit é famoso por ser <strong>muito</strong> complicado de lidar. Então dá pra imaginar o pesadelo que é manter o PhantomJS, que é basicamente manter um browser completo como Chrome ou Safari.</p>
<p>Não é de espantar que, quando o time do Chrome anunciou a disponibilidade do <a href="https://developers.google.com/web/updates/2017/04/headless-chrome"target="_blank" rel="noopener">Chrome Driver</a>, o mantenedor do PhantomJS <a href="https://github.com/teampoltergeist/poltergeist/issues/882"target="_blank" rel="noopener">decidiu se afastar do projeto</a>.</p>
<p>Se você conhece os contribuidores do PhantomJS, agradeça a eles — o projeto nos ajudou a construir funcionalidades de usuário mais sólidas por anos.</p>
<p>Dito isso, não precisa se preocupar. É perfeitamente possível substituir Poltergeist/PhantomJS por Selenium WebDriver/Chrome Driver no seu setup de RSpec/Capybara.</p>
<p>Meu amigo <a href="https://www.lucascaton.com.br/2017/06/22/how-to-run-your-feature-specs-using-capybara-and-headless-chrome/"target="_blank" rel="noopener">Lucas Caton</a> escreveu sobre isso em junho deste ano. Vale acompanhar o blog dele também.</p>
<p>Se você usa Linux com o browser Chromium, não precisa instalar nada — o Chrome Driver já vem com o Chromium. Caso contrário, instale os pacotes adequados para o seu sistema operacional. Por exemplo: <code>brew install chromedriver</code> no OS X, ou <code>pacaur -S chromedriver</code> no Arch Linux se preferir não ter o Chromium instalado. No Ubuntu, talvez seja necessário <a href="https://askubuntu.com/questions/539498/where-does-chromedriver-install-to"target="_blank" rel="noopener">ajustar seu PATH</a>.</p>
<p>Regra geral: instale o Chromium.</p>
<p>No meu caso, as mudanças foram as seguintes:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl"># Gemfile
</span></span><span class="line"><span class="cl"><span class="gd">- gem &#34;poltergeist&#34;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ gem &#34;selenium-webdriver&#34;
</span></span></span><span class="line"><span class="cl"><span class="gi">+ gem &#34;rspec-retry&#34;
</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Depois, na configuração do Capybara:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl">Capybara.server = :puma # Até o setup funcionar
</span></span><span class="line"><span class="cl">Capybara.server = :puma, { Silent: true } # Para limpar o output dos testes
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gd">- Capybara.register_driver :poltergeist do |app|
</span></span></span><span class="line"><span class="cl"><span class="gd">-   options = {
</span></span></span><span class="line"><span class="cl"><span class="gd">-     timeout: 3.minutes,
</span></span></span><span class="line"><span class="cl"><span class="gd">-     phantomjs_options: [
</span></span></span><span class="line"><span class="cl"><span class="gd">-       &#39;--proxy-type=none&#39;,
</span></span></span><span class="line"><span class="cl"><span class="gd">-       &#39;--load-images=no&#39;,
</span></span></span><span class="line"><span class="cl"><span class="gd">-       &#39;--ignore-ssl-errors=yes&#39;,
</span></span></span><span class="line"><span class="cl"><span class="gd">-       &#39;--ssl-protocol=any&#39;,
</span></span></span><span class="line"><span class="cl"><span class="gd">-       &#39;--web-security=false&#39;
</span></span></span><span class="line"><span class="cl"><span class="gd">-     ]
</span></span></span><span class="line"><span class="cl"><span class="gd">-   }
</span></span></span><span class="line"><span class="cl"><span class="gd">-   Capybara::Poltergeist::Driver.new(app, options)
</span></span></span><span class="line"><span class="cl"><span class="gd">- end
</span></span></span><span class="line"><span class="cl"><span class="gd">- Capybara.javascript_driver = :poltergeist
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gi">+ Capybara.register_driver :chrome do |app|
</span></span></span><span class="line"><span class="cl"><span class="gi">+   capabilities = Selenium::WebDriver::Remote::Capabilities.chrome(
</span></span></span><span class="line"><span class="cl"><span class="gi">+     chromeOptions: {
</span></span></span><span class="line"><span class="cl"><span class="gi">+       args: %w[ no-sandbox headless disable-popup-blocking disable-gpu window-size=1280,1024]
</span></span></span><span class="line"><span class="cl"><span class="gi">+     }
</span></span></span><span class="line"><span class="cl"><span class="gi">+   )
</span></span></span><span class="line"><span class="cl"><span class="gi">+ 
</span></span></span><span class="line"><span class="cl"><span class="gi">+   Capybara::Selenium::Driver.new(app, browser: :chrome, desired_capabilities: capabilities)
</span></span></span><span class="line"><span class="cl"><span class="gi">+ end
</span></span></span><span class="line"><span class="cl"><span class="gi">+ 
</span></span></span><span class="line"><span class="cl"><span class="gi">+ Capybara::Screenshot.register_driver :chrome do |driver, path|
</span></span></span><span class="line"><span class="cl"><span class="gi">+   driver.save_screenshot(path)
</span></span></span><span class="line"><span class="cl"><span class="gi">+ end
</span></span></span><span class="line"><span class="cl"><span class="gi">+ 
</span></span></span><span class="line"><span class="cl"><span class="gi">+ Capybara.javascript_driver = :chrome
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Capybara.default_max_wait_time = 5 # aumente esse timeout se sua aplicação for pesada para carregar
</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Em feature specs, às vezes o próprio Rails demora um pouco para subir, compilar assets e tal, e o primeiro spec pode dar timeout. Para evitar falha na execução dos testes, recomendo adicionar a gem <code>rspec-retry</code> como fiz acima, e incluir o seguinte no seu <code>spec/rails_helper.rb</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s1">&#39;rspec/retry&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="no">RSpec</span><span class="o">.</span><span class="n">configure</span> <span class="k">do</span> <span class="o">|</span><span class="n">config</span><span class="o">|</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># mostra o status de retry no processo do spec</span>
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">verbose_retry</span> <span class="o">=</span> <span class="kp">true</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Tenta duas vezes (repete uma vez)</span>
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">default_retry_count</span> <span class="o">=</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl">  <span class="c1"># Faz retry apenas quando o Selenium lança Net::ReadTimeout</span>
</span></span><span class="line"><span class="cl">  <span class="n">config</span><span class="o">.</span><span class="n">exceptions_to_retry</span> <span class="o">=</span> <span class="o">[</span><span class="no">Net</span><span class="o">::</span><span class="no">ReadTimeout</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E é isso. Não precisei alterar nenhum dos meus feature specs e todos rodaram perfeitamente. Parabéns às equipes que mantêm o Capybara e o Selenium-WebDriver por suportarem essa mudança.</p>
<p>Se você também desenvolve em Node.js, provavelmente já usou algo como o Casper, que dizem suportar Chrome Headless também. Mas já que estamos no assunto, vale muito conferir o <a href="https://github.com/GoogleChrome/puppeteer"target="_blank" rel="noopener">Puppeteer</a>, da própria equipe do Google. É uma biblioteca baseada em Promises onde você escreve código assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">puppeteer</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;puppeteer&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">(</span><span class="kr">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">browser</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">puppeteer</span><span class="p">.</span><span class="nx">launch</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">page</span> <span class="o">=</span> <span class="kr">await</span> <span class="nx">browser</span><span class="p">.</span><span class="nx">newPage</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="kr">await</span> <span class="nx">page</span><span class="p">.</span><span class="kr">goto</span><span class="p">(</span><span class="s1">&#39;https://example.com&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">screenshot</span><span class="p">({</span><span class="nx">path</span><span class="o">:</span> <span class="s1">&#39;example.png&#39;</span><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">await</span> <span class="nx">browser</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">})();</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Então sim, o Chrome Headless parece uma ótima opção — afinal, a maioria dos usuários já usa o Chrome, o que significa que teremos feature specs mais confiáveis e ferramentas de web crawling mais robustas.</p>
]]></content:encoded><category>rails</category><category>testes</category><category>tutoriais</category></item><item><title>Como Não Zerar sua Cota de Desenvolvimento no Pusher</title><link>https://akitaonrails.github.io/2017/10/27/como-nao-zerar-sua-cota-de-desenvolvimento-no-pusher/</link><guid isPermaLink="true">https://akitaonrails.github.io/2017/10/27/como-nao-zerar-sua-cota-de-desenvolvimento-no-pusher/</guid><pubDate>Fri, 27 Oct 2017 13:43:00 -0200</pubDate><description>&lt;p&gt;Se você está desenvolvendo com WebSockets (notificações em tempo real, chat em tempo real, etc), uma das melhores opções SaaS disponíveis ainda é o &lt;a href="pusher.com"&gt;&lt;strong&gt;Pusher&lt;/strong&gt;&lt;/a&gt;. Sempre foi confiável.&lt;/p&gt;
&lt;p&gt;Para cada aplicação que você cria, ele oferece ambientes separados de desenvolvimento, staging e produção, com tokens de chave/segredo independentes.&lt;/p&gt;
&lt;p&gt;Um problema que encontrei esses dias foi que eu estava zerando rapidamente a cota gratuita de mensagens do ambiente de desenvolvimento (200.000 mensagens por dia). O motivo era que toda a minha equipe usava a mesma chave, e o servidor de Integração Contínua fazia conexões reais sempre que rodava no mesmo ambiente. Isso consome tudo rapidamente e bloqueia tanto o desenvolvimento quanto o CI.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Se você está desenvolvendo com WebSockets (notificações em tempo real, chat em tempo real, etc), uma das melhores opções SaaS disponíveis ainda é o <a href="pusher.com"><strong>Pusher</strong></a>. Sempre foi confiável.</p>
<p>Para cada aplicação que você cria, ele oferece ambientes separados de desenvolvimento, staging e produção, com tokens de chave/segredo independentes.</p>
<p>Um problema que encontrei esses dias foi que eu estava zerando rapidamente a cota gratuita de mensagens do ambiente de desenvolvimento (200.000 mensagens por dia). O motivo era que toda a minha equipe usava a mesma chave, e o servidor de Integração Contínua fazia conexões reais sempre que rodava no mesmo ambiente. Isso consome tudo rapidamente e bloqueia tanto o desenvolvimento quanto o CI.</p>
<p>Na verdade, fazer conexões reais a sistemas externos em situações de teste não é boa prática. Os testes podem falhar aleatoriamente por vários motivos, então sempre devemos mockar essas dependências. Mas mockar um sistema complexo (WebSockets e HTTP) como o Pusher não é trivial.</p>
<p>Felizmente, encontrei o <a href="https://github.com/tristandunn/pusher-fake"target="_blank" rel="noopener">Pusher Fake</a>. Ele basicamente implementa todas as APIs e endpoints WebSocket necessários para imitar o Pusher e enganar tanto o servidor quanto o cliente JS para se comunicar com ele.</p>
<p>A ideia é que sua aplicação Rails, em modo de desenvolvimento, faça um fork de um processo separado de servidor para que o cliente Pusher se conecte. Essa gem é ao mesmo tempo um servidor clone do Pusher e uma série de wrappers para carregá-lo no seu setup.</p>
<p>Começando pelo básico.</p>
<p>Na sua aplicação, você terá tanto uma configuração de conexão do cliente Pusher no lado Ruby (para enviar mensagens a um canal no servidor Pusher) quanto uma instância Pusher do lado Javascript, no cliente, principalmente para assinar um canal em uma conexão WebSocket e receber mensagens.</p>
<p>Primeiro, precisamos configurar o lado Ruby. Geralmente fica em um <code>config/initializers/pusher.rb</code> assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">app_id</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;PUSHER_APP&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">key</span>    <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;PUSHER_KEY&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">secret</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;PUSHER_SECRET&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">cluster</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;PUSHER_CLUSTER&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="c1"># nunca defina Pusher.host ou Pusher.port aqui</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Note que estou usando variáveis de ambiente para guardar a configuração. Você deve usar algo como a <a href="https://github.com/laserlemon/figaro"target="_blank" rel="noopener">gem figaro</a> ou a <a href="https://github.com/bkeepers/dotenv"target="_blank" rel="noopener">gem dotenv-rails</a>. Por exemplo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">PUSHER_APP</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;xpto&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">PUSHER_KEY</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;abcd1234&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">PUSHER_SECRET</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;abcd1234&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">PUSHER_CLUSTER</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;us2&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>No mínimo você precisa de um ID de aplicação, uma chave, um segredo e um nome de cluster. Tudo isso é fornecido pelo Pusher quando você registra uma nova aplicação lá.</p>
<p>Segundo, precisamos configurar a instância Javascript. Geralmente você tem algo no diretório <code>assets/javascripts</code> assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// exemplo .js.erb
</span></span></span><span class="line"><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">pusher</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Pusher</span><span class="p">(</span><span class="o">&lt;%=</span> <span class="nx">ENV</span><span class="p">[</span><span class="s1">&#39;PUSHER_APP] %&gt;, {
</span></span></span><span class="line"><span class="cl"><span class="s1">  cluster: &lt;%= ENV[&#39;</span><span class="nx">PUSHER_KEY</span><span class="s1">&#39;] %&gt;,
</span></span></span><span class="line"><span class="cl"><span class="s1">  encrypted: &lt;%= ENV[&#39;</span><span class="nx">PUSHER_ENCRYPTED</span><span class="err">&#39;</span><span class="p">]</span> <span class="o">%&gt;</span><span class="p">})</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>No Console de Desenvolvimento do Chrome, você pode inspecionar essa instância digitando:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>Pusher.instances[0]</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Assim você consegue verificar se está pegando as configurações corretas para a conexão e depurar problemas.</p>
<p>As dependências são a gem pusher no seu <code>Gemfile</code> e o cliente javascript.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="c1"># Gemfile</span>
</span></span><span class="line"><span class="cl"><span class="n">gem</span> <span class="s1">&#39;pusher&#39;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>No caso do arquivo javascript, você pode adicioná-lo ao projeto e depender do <a href="http://www.akitaonrails.com/2017/10/24/iniciante-experimentando-o-rails-5-1-x"target="_blank" rel="noopener">Webpacker</a>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>yarn add pusher</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Então, no seu arquivo javascript ES6, você faz:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">Pusher</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="s1">&#39;pusher-js&#39;</span><span class="p">);</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Ou pode linkar diretamente no seu layout:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;https://js.pusher.com/4.2/pusher.min.js&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Para mais informações sobre o cliente pusher-js, leia o <a href="https://github.com/pusher/pusher-js"target="_blank" rel="noopener">arquivo README</a>.</p>
<h2>Adicionando o Pusher Fake<span class="hx:absolute hx:-mt-20" id="adicionando-o-pusher-fake"></span>
    <a href="#adicionando-o-pusher-fake" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Nesse ponto, você já deve conseguir se conectar à conta real do Pusher e ver a mágica do tempo real acontecendo.</p>
<p>E você já está consumindo a cota gratuita disponível no seu ambiente de desenvolvimento no Pusher. Para a maioria das pessoas, isso é suficiente.</p>
<p>Mas o objetivo é NÃO se conectar ao Pusher pela internet e manter tudo local para desenvolvimento e testes. Vamos começar adicionando o <a href="https://github.com/tristandunn/pusher-fake"target="_blank" rel="noopener">Pusher Fake</a> ao nosso <code>Gemfile</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">group</span> <span class="ss">:development</span><span class="p">,</span> <span class="ss">:test</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;pusher-fake&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora é onde a configuração do Pusher Fake pode ficar complicada se você não entender o que está acontecendo. Como disse antes, o PusherFake precisa fazer um fork de um novo processo para carregar um servidor local que imita o Pusher.</p>
<p>Para carregá-lo, você precisa apontar para o servidor local. Lembra do nosso <code>config/initializers/pusher.rb</code>? Basta fazer um require de um arquivo simples assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">app_id</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;PUSHER_APP&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">key</span>    <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;PUSHER_KEY&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">secret</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;PUSHER_SECRET&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">cluster</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;PUSHER_CLUSTER&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="no">Rails</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">development?</span>
</span></span><span class="line"><span class="cl">  <span class="nb">require</span> <span class="s2">&#34;pusher-fake/support/base&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Só isso já apresenta MUITOS problemas se você não tiver cuidado. Esse <code>require</code> vai <strong>fazer um fork</strong> de um novo processo. Se você estiver usando um servidor web de processo único como Webrick ou Thin, tudo bem. Se estiver usando Puma, Unicorn ou Passenger com no máximo um processo, também deve funcionar. Mas se você carregar um servidor web que por si mesmo faz fork de novos processos, vai ter problema.</p>
<p>Na prática, prefiro carregar o servidor Pusher Fake separadamente, em modo standalone. Felizmente ele fornece um comando de linha de comando para iniciá-lo. E é boa prática configurar isso em um arquivo <code>Procfile.dev</code> e usar o <a href="https://github.com/ddollar/foreman"target="_blank" rel="noopener">foreman</a> para iniciar tudo. O <code>Procfile.dev</code> fica assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>web: bundle exec rails s -p 3000
db: postgres -D /usr/local/var/postgres
redis: redis-server /usr/local/etc/redis.conf
mailcatcher: mailcatcher -f
pusherfake: pusher-fake -i ${PUSHER_APP:-xpto} --socket-host 0.0.0.0 --socket-port ${PUSHER_WS_PORT:-45449} --web-host 0.0.0.0 --web-port ${PUSHER_PORT:-8888} -k ${PUSHER_KEY:-abcd1234} -s ${PUSHER_SECRET:-abcd1234}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>De bônus, veja como configuro outros serviços como PostgreSQL, Redis, etc.</p>
<p>Se você não sabia, pode usar <code>${VARIABLE_NAME:-default_value}</code> para usar uma variável de ambiente ou ter um valor padrão caso ela não exista. Isso significa que suas variáveis de ambiente configuradas com Figaro ou Dotenv precisam ter os mesmos valores.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">PUSHER_APP</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;xpto&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">PUSHER_KEY</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;abcd1234&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">PUSHER_SECRET</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;abcd1234&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">PUSHER_CLUSTER</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;us2&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">PUSHER_HOST</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;127.0.0.1&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">PUSHER_PORT</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;8888&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">PUSHER_WS_HOST</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;127.0.0.1&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">PUSHER_WS_PORT</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;45449&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora seu <code>config/initializers/pusher.rb</code> deve ficar assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">app_id</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;PUSHER_APP&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">key</span>    <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;PUSHER_KEY&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">secret</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;PUSHER_SECRET&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="no">Pusher</span><span class="o">.</span><span class="n">cluster</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;PUSHER_CLUSTER&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="no">Rails</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">development?</span>
</span></span><span class="line"><span class="cl">  <span class="no">Pusher</span><span class="o">.</span><span class="n">host</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;PUSHER_HOST&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl">  <span class="no">Pusher</span><span class="o">.</span><span class="n">port</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;PUSHER_PORT&#39;</span><span class="o">]</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E a configuração do Pusher-js em algum lugar do seu diretório <code>app/assets/javascripts/</code> vai ficar algo assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="o">&lt;%</span> <span class="k">if</span> <span class="nx">defined</span><span class="o">?</span><span class="p">(</span><span class="nx">PusherFake</span><span class="p">)</span> <span class="o">%&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;%</span> <span class="k">if</span> <span class="nx">Rails</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">test</span><span class="o">?</span> <span class="o">%&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">var</span> <span class="nx">pusher</span> <span class="o">=</span> <span class="o">&lt;%=</span> <span class="nx">PusherFake</span><span class="p">.</span><span class="nx">javascript</span><span class="p">(</span><span class="nx">cluster</span><span class="o">:</span> <span class="nx">ENV</span><span class="p">[</span><span class="s2">&#34;PUSHER_CLUSTER&#34;</span><span class="p">])</span> <span class="o">%&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="o">&lt;%</span> <span class="k">else</span> <span class="o">%&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">window</span><span class="p">.</span><span class="nx">pusher</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Pusher</span><span class="p">(</span><span class="o">&lt;%=</span> <span class="nx">ENV</span><span class="p">[</span><span class="s1">&#39;PUSHER_KEY&#39;</span><span class="p">]</span> <span class="o">%&gt;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">cluster</span><span class="o">:</span> <span class="o">&lt;%=</span> <span class="nx">ENV</span><span class="p">[</span><span class="s1">&#39;PUSHER_CLUSTER&#39;</span><span class="p">]</span> <span class="o">%&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">wsHost</span><span class="o">:</span> <span class="o">&lt;%=</span> <span class="nx">ENV</span><span class="p">[</span><span class="s1">&#39;PUSHER_WS_HOST&#39;</span><span class="p">]</span> <span class="o">%&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">wsPort</span><span class="o">:</span> <span class="o">&lt;%=</span> <span class="nx">ENV</span><span class="p">[</span><span class="s1">&#39;PUSHER_WS_PORT&#39;</span><span class="p">]</span> <span class="o">%&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">encrypted</span><span class="o">:</span> <span class="o">&lt;%=</span> <span class="nx">ENV</span><span class="p">[</span><span class="s1">&#39;PUSHER_ENCRYPTED&#39;</span><span class="p">]</span> <span class="o">%&gt;</span><span class="p">})</span>    
</span></span><span class="line"><span class="cl">    <span class="o">&lt;%</span> <span class="nx">end</span> <span class="o">%&gt;</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;%</span> <span class="k">else</span> <span class="o">%&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">window</span><span class="p">.</span><span class="nx">pusher</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Pusher</span><span class="p">(</span><span class="o">&lt;%=</span> <span class="nx">ENV</span><span class="p">[</span><span class="s1">&#39;PUSHER_KEY&#39;</span><span class="p">]</span> <span class="o">%&gt;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">cluster</span><span class="o">:</span> <span class="o">&lt;%=</span> <span class="nx">ENV</span><span class="p">[</span><span class="s1">&#39;PUSHER_CLUSTER&#39;</span><span class="p">]</span> <span class="o">%&gt;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">encrypted</span><span class="o">:</span> <span class="o">&lt;%=</span> <span class="nx">ENV</span><span class="p">[</span><span class="s1">&#39;PUSHER_ENCRYPTED&#39;</span><span class="p">]</span> <span class="o">%&gt;</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="o">&lt;%</span> <span class="nx">end</span> <span class="o">%&gt;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Lembre que este é um arquivo Javascript+ERB, então podemos buscar as mesmas variáveis de ambiente para a configuração.</p>
<p>Agora, sempre que você executar <code>foreman start -f Procfile.dev -p 3000</code>, ele vai carregar o servidor Pusher Fake com as configurações de desenvolvimento corretas, e tanto o seu servidor ruby quanto o cliente javascript devem se conectar a ele sem nenhum problema.</p>
<p>Note também o trecho <code>if Rails.env.test?</code>. Isso é para a sua suite de testes RSpec. No caso do ambiente de testes, não vamos carregar o servidor fake manualmente; em vez disso, vamos criar algo como <code>spec/support/pusher-fake.rb</code> com:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="nb">require</span> <span class="s2">&#34;pusher-fake/support/rspec&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E é isso. O <code>PusherFake.javascript</code> vai definir a configuração padrão de conexão WebSocket, e o <code>require</code> acima vai tanto fazer o fork do servidor fake quanto configurar o RSpec para limpar os canais a cada execução de teste (via <code>PusherFake::Channel.reset</code>).</p>
<p>Desta forma, seu ambiente de testes também evita se conectar ao servidor Pusher real, externo.</p>
<p>A chave de tudo isso são as variáveis de ambiente. Você precisa garantir que cada peça esteja carregando a mesma configuração — caso contrário, você terá o servidor fake fazendo bind em uma porta diferente da que o Pusher-js está tentando se conectar, e vai ter erros. Depure com cuidado.</p>
<p>O mais importante: se você fez tudo corretamente, agora é independente do servidor Pusher real para os ambientes de desenvolvimento e testes, nunca vai atingir nenhum limite de cota, e sua equipe e seu CI poderão trabalhar de forma ininterrupta, com comportamento determinístico.</p>
]]></content:encoded><category>rails</category><category>testes</category><category>tutoriais</category></item><item><title>Checklist de Deploy do Rails 5.1 no Heroku</title><link>https://akitaonrails.github.io/2017/06/28/checklist-de-deploy-do-rails-5-1-no-heroku/</link><guid isPermaLink="true">https://akitaonrails.github.io/2017/06/28/checklist-de-deploy-do-rails-5-1-no-heroku/</guid><pubDate>Wed, 28 Jun 2017 15:12:00 -0300</pubDate><description>&lt;p&gt;Lancei &lt;a href="http://www.theconf.club"target="_blank" rel="noopener"&gt;THE CONF&lt;/a&gt; ontem. Espero que você aproveite a grade da conferência e aproveite o desconto early-bird limitado!&lt;/p&gt;
&lt;p&gt;Enfim, o site em si é super simples. Uma página única. Escolhi usar Rails 5.1 como estrutura porque ele cuida de tudo que eu teria que adicionar manualmente em qualquer outro framework. Especialmente agora que traz suporte nativo a Yarn e Webpack, fica fácil de usar para qualquer dev front-end competente.&lt;/p&gt;
&lt;p&gt;Mas mesmo com todas as facilidades embutidas, um setup completo de produção ainda exige etapas extras que a maioria dos iniciantes não conhece. Por isso resolvi compilar um pequeno checklist do que você precisa cuidar antes de fazer deploy em produção. Não é uma lista exaustiva para todos os casos de uso, mas deve ser suficiente para a maioria dos projetos menores.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Lancei <a href="http://www.theconf.club"target="_blank" rel="noopener">THE CONF</a> ontem. Espero que você aproveite a grade da conferência e aproveite o desconto early-bird limitado!</p>
<p>Enfim, o site em si é super simples. Uma página única. Escolhi usar Rails 5.1 como estrutura porque ele cuida de tudo que eu teria que adicionar manualmente em qualquer outro framework. Especialmente agora que traz suporte nativo a Yarn e Webpack, fica fácil de usar para qualquer dev front-end competente.</p>
<p>Mas mesmo com todas as facilidades embutidas, um setup completo de produção ainda exige etapas extras que a maioria dos iniciantes não conhece. Por isso resolvi compilar um pequeno checklist do que você precisa cuidar antes de fazer deploy em produção. Não é uma lista exaustiva para todos os casos de uso, mas deve ser suficiente para a maioria dos projetos menores.</p>
<ul>
<li><a href="#setup">Configurando o projeto</a></li>
<li><a href="#cdn">Adicionando CDN e configurando suporte a CORS</a> (para carregar fontes, por exemplo)</li>
<li><a href="#cache">Adicionando Memcached</a></li>
</ul>
<p><a name="setup"></a></p>
<h2>Configurando o projeto<span class="hx:absolute hx:-mt-20" id="configurando-o-projeto"></span>
    <a href="#configurando-o-projeto" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Uma coisa que a maioria das pessoas esquece, até desenvolvedores experientes, é que o comando <code>rails new</code> usado para criar a estrutura inicial do projeto aceita várias flags. Em vez de ter que ajustar arquivos manualmente depois, você pode começar assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>rails new your_project \
--database=postgresql \
--webpack=react \
--skip-action-cable \
--skip-coffee
--skip-turbolinks</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Se você está construindo com React ou outro framework JavaScript completo, provavelmente vai querer pular o Turbolinks. Caso contrário, se for um site simples, use <a href="https://sevos.io/2017/02/27/turbolinks-lifecycle-explained.html"target="_blank" rel="noopener">Turbolinks</a>.</p>
<p>Comece usando Postgresql desde o início, não use Sqlite3.</p>
<p>Pule o Action Cable. Prefira uma solução real como o <a href="https://pusher.com/"target="_blank" rel="noopener">Pusher.com</a>. Se você realmente precisar de algo interno, considere algo como minha solução com Elixir, o <a href="http://expusherlite.cm42.io/"target="_blank" rel="noopener">Ex Pusher Lite</a>. Leve essa recomendação com cautela, claro — para coisas pequenas o Action Cable é mais que suficiente. Posso escrever outro post elaborando esse ponto se as pessoas indicarem interesse nos comentários.</p>
<p>Enfim, me perdi. Certifique-se de ter 2 arquivos de boot: primeiro o <code>Procfile</code> canônico, usado pelo Heroku em produção:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>web: bin/rails server -p $PORT -b 0.0.0.0</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Segundo, um <code>Procfile.dev</code> para usar apenas no ambiente de desenvolvimento:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>web: ./bin/rails server
webpacker: ./bin/webpack-dev-server</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>É assim que você sobe o servidor webpack que vai compilar seus assets em tempo real durante o desenvolvimento. Lembre também de rodar esses dois comandos de dependência:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>yarn install
bundle install</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Instale dependências JavaScript com <code>yarn add [pacote]</code> e pronto! Em produção você não usa o servidor webpack (por isso não o adicionamos ao <code>Procfile</code> de produção) — o Heroku detecta automaticamente a gem <a href="https://github.com/rails/webpacker"target="_blank" rel="noopener">webpacker</a>, instala o buildpack do nodejs, roda <code>yarn install</code> por você, e quando <code>rails assets:precompile</code> executa ele também dispara <code>yarn run</code>, que pré-compila todos os assets (JavaScript, folhas de estilo, imagens) com fingerprinting correto para cache busting e tudo mais que já estamos acostumados no Asset Pipeline do Rails.</p>
<p>No Heroku, basicamente você não precisa fazer nada. E num servidor de deploy customizado basta lembrar de rodar a task <code>assets:precompile</code> e deixar ela cuidar de tudo.</p>
<p><a name="cdn"></a></p>
<h3>Adicionando CDN e configurando CORS<span class="hx:absolute hx:-mt-20" id="adicionando-cdn-e-configurando-cors"></span>
    <a href="#adicionando-cdn-e-configurando-cors" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Isso é realmente simples. Não há razão para não adicionar uma CDN a qualquer site. Simplesmente faça.</p>
<p>Abra seu AWS Management Console e <a href="https://console.aws.amazon.com/cloudfront/"target="_blank" rel="noopener">abra o CloudFront</a>. Clique em &ldquo;Create Distribution&rdquo; e siga os padrões do wizard. O requisito é que você DEVE saber o domínio e subdomínio para onde quer apontar (por exemplo, &ldquo;<a href="https://www.theconf.club"target="_blank" rel="noopener">www.theconf.club</a>&rdquo;) no campo &ldquo;Origin Domain Name&rdquo;.</p>
<p>A única customização que você DEVE fazer é mudar a opção <a href="http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/header-caching.html#header-caching-web-cors"target="_blank" rel="noopener">&ldquo;Forward Headers&rdquo;</a> para &ldquo;Whitelist&rdquo; em &ldquo;Default Cache Behavior Settings&rdquo;. Depois adicione &ldquo;Origin&rdquo;, &ldquo;Access-Control-Request-Headers&rdquo; e &ldquo;Access-Control-Request-Method&rdquo; à Whitelist. Pronto, sua distribuição está habilitada para CORS.</p>
<p>Vai demorar um pouco para criar (precisa configurar vários data centers ao redor do mundo), mas você vai terminar com uma URL representando sua distribuição, algo como <code>doz7rtw2u3wg4.cloudfront.net</code>. Recomendo adicioná-la como variável de ambiente no Heroku assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>heroku config:set CDN_URL=doz7rtw2u3wg4.cloudfront.net</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora edite seu <code>config/environments/production.rb</code> e adicione:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">config</span><span class="o">.</span><span class="n">action_controller</span><span class="o">.</span><span class="n">asset_host</span> <span class="o">=</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;CDN_URL&#39;</span><span class="o">]</span> <span class="k">if</span> <span class="no">ENV</span><span class="o">[</span><span class="s1">&#39;CDN_URL&#39;</span><span class="o">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Para usar a CDN de fato, você deve declarar cada asset utilizado nos templates de view usando os Rails View Helpers como <code>image_tag</code>, <code>asset_path</code>, <code>javascript_pack_tag</code>, <code>stylesheed_pack_tag</code>, <code>stylesheet_link_tag</code>, etc. O bootstrap do Rails já cria o template de layout com esses helpers, basta seguir o padrão.</p>
<p>Quando o webpack rodar, vai gerar todos os assets estáticos, otimizados e pré-compilados em <code>public/packs</code> com um arquivo de manifesto declarando a URL completa apontando para a CDN. Por exemplo, se eu buscar <code>/app/public/packs/manifest.json</code> diretamente do dyno do Heroku, terei algo assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>{
  &#34;Roboto-Bold.woff&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/Roboto-Bold-eed9aab5449cc9c8430d7d258108f602.woff&#34;,
  &#34;Roboto-Bold.woff2&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/Roboto-Bold-c0f1e4a4fdfb8048c72e86aadb2a247d.woff2&#34;,
  &#34;Roboto-Light.woff&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/Roboto-Light-ea36cd9a0e9eee97012a67b8a4570d7b.woff&#34;,
  &#34;Roboto-Light.woff2&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/Roboto-Light-3c37aa69cd77e6a53a067170fa8fe2e9.woff2&#34;,
  &#34;Roboto-Medium.woff&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/Roboto-Medium-cf4d60bc0b1d4b2314085919a00e1724.woff&#34;,
  &#34;Roboto-Medium.woff2&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/Roboto-Medium-1561b424aaef2f704bbd89155b3ce514.woff2&#34;,
  &#34;Roboto-Regular.woff&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/Roboto-Regular-3cf6adf61054c328b1b0ddcd8f9ce24d.woff&#34;,
  &#34;Roboto-Regular.woff2&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/Roboto-Regular-5136cbe62a63604402f2fedb97f246f8.woff2&#34;,
  &#34;Roboto-Thin.woff&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/Roboto-Thin-44b78f142603eb69f593ed4002ed7a4a.woff&#34;,
  &#34;Roboto-Thin.woff2&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/Roboto-Thin-1f35e6a11d27d2e10d28946d42332dc5.woff2&#34;,
  &#34;application.css&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/application-09b53ce9ca3dd595ee99.css&#34;,
  &#34;application.css.map&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/application-09b53ce9ca3dd595ee99.css.map&#34;,
  &#34;application.js&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/application-799300612ff6d6595198.js&#34;,
  &#34;application.js.map&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/application-799300612ff6d6595198.js.map&#34;,
  &#34;home_page.js&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/home_page-ff3b49407a1d01592ad5.js&#34;,
  &#34;home_page.js.map&#34;: &#34;//doz7rtw2u3wg4.cloudfront.net/packs/home_page-ff3b49407a1d01592ad5.js.map&#34;
}</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Então, se por algum motivo você precisar criar uma nova distribuição CDN, lembre de atualizar a variável <code>CDN_URL</code> no Heroku e fazer redeploy da aplicação para que os assets e esse arquivo de manifesto sejam regerados. Ele vai usar essas URLs ao renderizar os HTMLs finais.</p>
<p>Quando um usuário abre seu site, recebe o HTML com URLs apontando para a CDN. Na primeira vez não haverá nenhum asset em cache, então a CDN vai buscar no seu servidor. Seu servidor deve retornar os assets COM os headers CORS corretos para que a CDN os armazene em cache junto com os headers e os repasse ao browser. O browser precisa desses headers porque vai abrir do domínio <code>www.theconf.club</code>, por exemplo, mas as fontes estão sendo carregadas de <code>doz7rtw2u3wg4.cloudfront.net</code> — o que geraria um aviso de segurança e bloquearia o carregamento das fontes por serem de domínios diferentes. Por isso os headers CORS indicam que é seguro carregá-las.</p>
<p>Para que sua aplicação Rails retorne os headers corretos, adicione a gem <code>rack-cors</code> ao Gemfile. Depois crie um arquivo de configuração em <code>config/initializers/rack-cors.rb</code> com:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">if</span> <span class="n">defined?</span> <span class="no">Rack</span><span class="o">::</span><span class="no">Cors</span>
</span></span><span class="line"><span class="cl">  <span class="no">Rails</span><span class="o">.</span><span class="n">configuration</span><span class="o">.</span><span class="n">middleware</span><span class="o">.</span><span class="n">insert_before</span> <span class="mi">0</span><span class="p">,</span> <span class="no">Rack</span><span class="o">::</span><span class="no">Cors</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">allow</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="n">origins</span> <span class="sx">%w[
</span></span></span><span class="line"><span class="cl"><span class="sx">        https://theconf.club
</span></span></span><span class="line"><span class="cl"><span class="sx">        http://theconf.club
</span></span></span><span class="line"><span class="cl"><span class="sx">        https://www.theconf.club
</span></span></span><span class="line"><span class="cl"><span class="sx">        http://www.theconf.club
</span></span></span><span class="line"><span class="cl"><span class="sx">        https://theconf.herokuapp.com
</span></span></span><span class="line"><span class="cl"><span class="sx">        http://theconf.herokuapp.com
</span></span></span><span class="line"><span class="cl"><span class="sx">      ]</span>
</span></span><span class="line"><span class="cl">      <span class="n">resource</span> <span class="s1">&#39;/assets/*&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Quando fizer o deploy, você sabe que está funcionando quando faz um curl num asset e ele retorna os headers Access-* assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ curl -I -s -X GET -H &#34;Origin: www.theconf.club&#34; http://www.theconf.club/packs/Roboto-Regular-5136cbe62a63604402f2fedb97f246f8.woff2
HTTP/1.1 200 OK
Server: Cowboy
Date: Wed, 28 Jun 2017 17:44:41 GMT
Connection: keep-alive
Access-Control-Allow-Origin: www.theconf.club
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Expose-Headers: 
Access-Control-Max-Age: 1728000
Access-Control-Allow-Credentials: true
Last-Modified: Wed, 28 Jun 2017 17:06:03 GMT
Content-Type: application/font-woff2
Cache-Control: public, max-age=2592000
Vary: Origin
Content-Length: 64832
Via: 1.1 vegur</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E se tudo estiver configurado, você deve ver os headers sendo repassados pela CDN assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ curl -I -s -X GET -H &#34;Origin: www.theconf.club&#34; http://doz7rtw2u3wg4.cloudfront.net/packs/Roboto-Regular-5136cbe62a63604402f2fedb97f246f8.woff2
HTTP/1.1 200 OK
Content-Type: application/font-woff2
Content-Length: 64832
Connection: keep-alive
Server: Cowboy
Date: Wed, 28 Jun 2017 17:45:18 GMT
Access-Control-Allow-Origin: www.theconf.club
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Expose-Headers: 
Access-Control-Max-Age: 1728000
Access-Control-Allow-Credentials: true
Last-Modified: Wed, 28 Jun 2017 17:06:03 GMT
Cache-Control: public, max-age=2592000
Via: 1.1 vegur, 1.1 86e9abdb4c15b9d3a542f9b93245e87e.cloudfront.net (CloudFront)
Vary: Origin
X-Cache: Miss from cloudfront
X-Amz-Cf-Id: tVkZ41RRr66iBT6atWTO_oeTY_jG0zCBFuXU8bKyClZDQ8kl-hDegA==</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Uma CDN é o ingrediente secreto que permite a qualquer site baseado em conteúdo escalar muito além do que seu servidor consegue. É uma economia enorme e ainda proporciona uma experiência muito mais suave para seus usuários.</p>
<p>Um último ponto. Os Rails View Helpers como <code>image_tag</code> permitem que você informe o nome do arquivo de imagem sem extensão em desenvolvimento e ele encontra a imagem corretamente. Mas no servidor vai falhar ao renderizar o template dessa forma. Como regra geral, <strong>SEMPRE</strong> informe o nome completo do arquivo com extensão, por exemplo <code>image_tag(&quot;logo.png&quot;)</code> em vez de apenas <code>image_tag(&quot;logo&quot;)</code>.</p>
<p>Você pode ver como isso falha abrindo um console no Heroku:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>$ heroku run rails c                                                                                                                  
Running rails c on ⬢ theconf... up, run.8271 (Hobby)
Loading production environment (Rails 5.1.2)

irb(main):001:0&gt; ActionController::Base.helpers.asset_path(&#34;icon-goals&#34;)
Sprockets::Rails::Helper::AssetNotFound: The asset &#34;icon-goals&#34; is not present in the asset pipeline.
    from (irb):1

irb(main):002:0&gt; ActionController::Base.helpers.asset_path(&#34;icon-goals.png&#34;)
=&gt; &#34;//d134ipy19a646x.cloudfront.net/assets/icon-goals-b969b3b7325d33ad85a88dbb5b894832909ed738eea9964b9cf535646b93674b.png&#34;</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><a name="cache"></a></p>
<h3>Adicionando Memcached<span class="hx:absolute hx:-mt-20" id="adicionando-memcached"></span>
    <a href="#adicionando-memcached" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Falar sobre cache é sempre complicado. É por isso que muita gente nem tenta. Mesmo que você possa se aventurar em configurações bem granulares como o <a href="http://edgeguides.rubyonrails.org/caching_with_rails.html#russian-doll-caching"target="_blank" rel="noopener">Russian Doll Caching</a>, adicionar cache em alguns pontos estratégicos já traz um benefício enorme. E é super fácil de configurar.</p>
<p>O primeiro passo é adicionar o <a href="https://devcenter.heroku.com/articles/memcachier"target="_blank" rel="noopener">Memcachier</a> à sua aplicação Heroku. Ele tem um tier gratuito e para a maioria dos apps pequenos é o suficiente.</p>
<p>A configuração também é trivial. Comece adicionando as seguintes gems ao seu <code>Gemfile</code>:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">group</span> <span class="ss">:production</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;rack-cors&#39;</span><span class="p">,</span> <span class="nb">require</span><span class="p">:</span> <span class="s1">&#39;rack/cors&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;rack-cache&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;dalli&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;kgio&#39;</span>
</span></span><span class="line"><span class="cl">  <span class="n">gem</span> <span class="s1">&#39;memcachier&#39;</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Agora edite seu <code>config/environments/production.rb</code> assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="n">config</span><span class="o">.</span><span class="n">cache_store</span> <span class="o">=</span> <span class="ss">:dalli_store</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">client</span> <span class="o">=</span> <span class="no">Dalli</span><span class="o">::</span><span class="no">Client</span><span class="o">.</span><span class="n">new</span><span class="p">((</span><span class="no">ENV</span><span class="o">[</span><span class="s2">&#34;MEMCACHIER_SERVERS&#34;</span><span class="o">]</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">)</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&#34;,&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">                           <span class="ss">:username</span> <span class="o">=&gt;</span> <span class="no">ENV</span><span class="o">[</span><span class="s2">&#34;MEMCACHIER_USERNAME&#34;</span><span class="o">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                           <span class="ss">:password</span> <span class="o">=&gt;</span> <span class="no">ENV</span><span class="o">[</span><span class="s2">&#34;MEMCACHIER_PASSWORD&#34;</span><span class="o">]</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                           <span class="ss">:failover</span> <span class="o">=&gt;</span> <span class="kp">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                           <span class="ss">:socket_timeout</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="o">.</span><span class="mi">5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                           <span class="ss">:socket_failure_delay</span> <span class="o">=&gt;</span> <span class="mi">0</span><span class="o">.</span><span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                           <span class="ss">:value_max_bytes</span> <span class="o">=&gt;</span> <span class="mi">10485760</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">config</span><span class="o">.</span><span class="n">action_dispatch</span><span class="o">.</span><span class="n">rack_cache</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:metastore</span>    <span class="o">=&gt;</span> <span class="n">client</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="ss">:entitystore</span>  <span class="o">=&gt;</span> <span class="n">client</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Digamos que você tenha um bloco no template que requer vários registros do banco de dados, mas você sabe que esses registros mal mudam. O que fazer? Uma alternativa é fazer cache da query ActiveRecord inteira assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ruby" data-lang="ruby"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">HomePageController</span> <span class="o">&lt;</span> <span class="no">ApplicationController</span>
</span></span><span class="line"><span class="cl">  <span class="k">def</span> <span class="nf">index</span>
</span></span><span class="line"><span class="cl">    <span class="vi">@selected_proposals</span> <span class="o">=</span> <span class="no">Rails</span><span class="o">.</span><span class="n">cache</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="s1">&#39;selected_proposals&#39;</span><span class="p">,</span> <span class="ss">expires_in</span><span class="p">:</span> <span class="mi">1</span><span class="o">.</span><span class="n">day</span><span class="p">)</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">      <span class="no">Proposal</span><span class="o">.</span><span class="n">includes</span><span class="p">(</span><span class="ss">:authors</span><span class="p">)</span><span class="o">.</span><span class="n">where</span><span class="p">(</span><span class="ss">selected</span><span class="p">:</span> <span class="kp">true</span><span class="p">)</span><span class="o">.</span><span class="n">to_a</span>
</span></span><span class="line"><span class="cl">    <span class="k">end</span>
</span></span><span class="line"><span class="cl">  <span class="k">end</span>
</span></span><span class="line"><span class="cl"><span class="k">end</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>O <code>#to_a</code> é necessário porque queries ActiveRecord são lazy. O <code>#to_a</code> força a busca e o resultado fica em cache. Da próxima vez, não vai tocar no banco de dados por um dia inteiro!</p>
<p>Também poderia ter adicionado um bloco <code>cache do ... end</code> no próprio template. Há muitas opções. O ponto é que não é tão difícil quanto a maioria das pessoas imagina.</p>
<p>O que torna o cache difícil é a lógica de expiração. E a regra geral é: nunca tente expirar cache manualmente. Mude a chave de busca para outra coisa e deixe os dados antigos morrerem (o memcached cuida de eliminar os dados antigos não utilizados).</p>
<p>Vale muito a pena ler o <a href="http://edgeguides.rubyonrails.org/caching_with_rails.html"target="_blank" rel="noopener">Rails Guides on Caching</a>. Realmente não é tão difícil quanto você pensa, e você pode cachear apenas os trechos onde sabe que há sensibilidade de performance, deixando as partes altamente dinâmicas sem cache.</p>
<p>Como é super barato, use agora mesmo.</p>
<p><a name="ssl"></a></p>
<h3>Adicionando suporte a SSL<span class="hx:absolute hx:-mt-20" id="adicionando-suporte-a-ssl"></span>
    <a href="#adicionando-suporte-a-ssl" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Se você tem qualquer transação sensível à segurança (ex. compras), use SSL. Muita gente evita porque costuma ser difícil até entender como obter um certificado corretamente.</p>
<p>O <a href="https://letsencrypt.org/"target="_blank" rel="noopener">Let&rsquo;s Encrypt</a> tornou o processo super simples. Parabéns a eles! E melhor ainda: é gratuito! Então não há desculpa para não ter SSL.</p>
<p>E no Heroku é <a href="https://devcenter.heroku.com/articles/automated-certificate-management"target="_blank" rel="noopener">ainda mais fácil</a>!</p>
<blockquote>
  <p>O ACM (Automated Certificate Management) está habilitado por padrão para todas as aplicações criadas após 21 de março de 2017 rodando em dynos Hobby ou Professional. Os passos a seguir são para aplicações rodando em Free dynos e para aplicações criadas antes dessa data.</p>

</blockquote>
<p>Nos Free Tier Dynos, é assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>heroku certs:auto:enable</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Verifique o status com <code>heroku certs:auto</code>.</p>
<p>Pronto, não há passo 2.</p>
<p>Antes tínhamos que usar a complicada gem <a href="https://github.com/pixielabs/letsencrypt-rails-heroku"target="_blank" rel="noopener">letsencrypt-rails-heroku</a>, mas agora está simples demais.</p>
<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Acredito que isso cobre o básico do que você deve fazer antes de fazer deploy de um app pequeno no Heroku. Para apps maiores há muito mais preocupações que estão além do escopo deste post.</p>
<p>Se você lembrar de mais dicas e truques, compartilhe na seção de comentários abaixo.</p>
]]></content:encoded><category>rails</category><category>devops</category><category>tutoriais</category></item><item><title>Começando com Blender</title><link>https://akitaonrails.github.io/2017/06/21/comecando-com-blender/</link><guid isPermaLink="true">https://akitaonrails.github.io/2017/06/21/comecando-com-blender/</guid><pubDate>Wed, 21 Jun 2017 17:32:00 -0300</pubDate><description>&lt;p&gt;O Blender é uma fera. Uma verdadeira maravilha do que o software open source consegue alcançar, e você deveria aplaudir de pé todos que ajudaram a construir algo que funciona tão bem assim. Ele rivaliza com as opções comerciais mais caras do mercado, do Maya ao venerável Renderman da Pixar.&lt;/p&gt;
&lt;p&gt;A comunidade do Blender é tão apaixonada e comprometida que frequentemente produz &lt;a href="https://web.archive.org/web/20151127160645/http://archive.blender.org/features-gallery/movies/"target="_blank" rel="noopener"&gt;curtas-metragens&lt;/a&gt; de alta qualidade, com nível quase hollywoodiano, usando o próprio Blender. A ideia é justamente estressar a ferramenta e identificar gargalos e problemas de usabilidade num fluxo de trabalho real.&lt;/p&gt;</description><content:encoded><![CDATA[<p>O Blender é uma fera. Uma verdadeira maravilha do que o software open source consegue alcançar, e você deveria aplaudir de pé todos que ajudaram a construir algo que funciona tão bem assim. Ele rivaliza com as opções comerciais mais caras do mercado, do Maya ao venerável Renderman da Pixar.</p>
<p>A comunidade do Blender é tão apaixonada e comprometida que frequentemente produz <a href="https://web.archive.org/web/20151127160645/http://archive.blender.org/features-gallery/movies/"target="_blank" rel="noopener">curtas-metragens</a> de alta qualidade, com nível quase hollywoodiano, usando o próprio Blender. A ideia é justamente estressar a ferramenta e identificar gargalos e problemas de usabilidade num fluxo de trabalho real.</p>
<p>Este post é, principalmente, para o &ldquo;eu do futuro&rdquo; poder voltar a uma lista de recursos centralizada. Como modelagem 3D não é meu trabalho em tempo integral, vou ter longos intervalos entre as sessões com o Blender. E eu sei que vou me arrepender se não descarregar o que aprendi num post enquanto ainda está fresco :-)</p>
<h3>Primeiros Passos para Iniciantes<span class="hx:absolute hx:-mt-20" id="primeiros-passos-para-iniciantes"></span>
    <a href="#primeiros-passos-para-iniciantes" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Antes de mais nada: se você está neste blog, provavelmente é programador. E deixa eu te dizer que o pessoal de gráficos tem uma visão de &ldquo;usabilidade&rdquo; bem diferente da nossa. A quantidade absurda de customizações, opções, atalhos e combinações rivaliza até com usuários de Vim e Emacs.</p>
<p>Ah, e a propósito: você vai precisar de um mouse com 3 botões de verdade. Touchpad não serve pra nada no Blender, e o mouse do Mac é horrível. <a href="http://www.dell.com/br/mouse"target="_blank" rel="noopener">Qualquer mouse barato de PC resolve.</a></p>
<p>Por padrão, o botão principal é o da direita, não o da esquerda como estamos acostumados. Mude isso nas <a href="https://web.archive.org/web/20181118204135/https://docs.blender.org/manual/en/dev/preferences/input.html"target="_blank" rel="noopener">preferências do usuário</a> para selecionar com o botão esquerdo, e aí as coisas começam a fazer sentido. Enquanto estiver lá, habilite também a emulação do Numpad.</p>
<p>Aliás, vale muito a pena ter um teclado com numpad ou até um numpad separado. Você pode usar a fileira de números do teclado normal, mas o Blender foi projetado presumindo tanto um mouse de 3 botões invertido quanto um numpad.</p>
<p><a href="https://web.archive.org/web/20181118204135/https://docs.blender.org/manual/en/dev/preferences/input.html"target="_blank" rel="noopener"><img src="https://web.archive.org/web/20181119110123/https://docs.blender.org/manual/en/dev/_images/preferences_input_tab.png" alt="Preferências de Input"  loading="lazy" /></a></p>
<p>Se você nunca estudou Blender antes, não vai conseguir descobrir nada explorando a interface por conta própria. A UI é inútil até alguém te ensinar o básico. Há centenas de termos que você simplesmente não vai entender, como Meshes, Edges, Seams, Nodes, Viewport, Subsurface, Modifiers, etc.</p>
<p>A coisa mais importante que você PRECISA fazer antes de continuar é assistir essa série completa de 9 partes do Blender Guru, chamada <a href="https://www.youtube.com/watch?v=VT5oZndzj68&amp;list=PLjEaoINr3zgHs8uzT3yqe4iHGfkCmMJ0P"target="_blank" rel="noopener">Blender Beginner Tutorial Series</a>. Opcionalmente, você pode fazer depois, com calma, os <a href="https://www.youtube.com/watch?v=Mwzz-Y6t-v8&amp;list=PLjEaoINr3zgEgoyYWE0Yit-cVoZ60WGtt"target="_blank" rel="noopener">Intermediate Blender Tutorials</a>.</p>
<p>A série para iniciantes vai te ensinar o suficiente para você finalmente começar a se sentir confortável com a interface e as ferramentas. E não esqueça de imprimir e pendurar esse <a href="https://web.archive.org/web/20170708042817/https://www.blenderguru.com/articles/free-blender-keyboard-shortcut-pdf"target="_blank" rel="noopener">&ldquo;cheat sheet&rdquo;</a> tão útil. Você não vai acreditar como a vida fica mais fácil quando incorpora os atalhos de teclado mais importantes.</p>
<h3>Configurações Padrão Melhores<span class="hx:absolute hx:-mt-20" id="configurações-padrão-melhores"></span>
    <a href="#configura%c3%a7%c3%b5es-padr%c3%a3o-melhores" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Por razões históricas, algumas configurações padrão não são as ideais. O conhecimento na área de renderização 3D evolui muito rápido.</p>
<p>A primeira coisa a fazer: <a href="https://web.archive.org/web/20160308012834/http://wiki.blender.org/index.php/Doc:2.6/Tutorials/Rendering/Cycles"target="_blank" rel="noopener">trocar</a> o motor de renderização padrão de Blender Render para o Cycles Raytracing Engine.</p>
<p>Depois, tratamento de cor. O sRGB EOTF padrão é basicamente errado. Você precisa baixar a configuração <a href="https://sobotka.github.io/filmic-blender/"target="_blank" rel="noopener">filmic-blender</a> do Sobotka. No Arch Linux, basta fazer:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>pacaur -S filmic-blender-git</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Criei um script em <code>~/bin/filmic-blender</code> com o seguinte:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>env OCIO=/usr/share/blender/2.78/datafiles/filmic-blender/config.ocio blender</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>E sempre inicializo o Blender pelo terminal assim:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><pre><code>optirun ~/bin/filmic-blender</code></pre></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Isso faz duas coisas: primeiro, pré-configura o OpenColorIO para usar o substituto Filmic. Segundo, habilita a GPU externa do meu notebook para uso no Blender. Leia meu post <a href="http://www.akitaonrails.com/2017/03/14/habilitando-a-gpu-optimus-nvidia-no-dell-xps-15-com-linux"target="_blank" rel="noopener">&ldquo;Enabling Optimus NVIDIA GPU on the Dell XPS 15 with Linux, even on Battery&rdquo;</a> para mais detalhes. No Windows ou Mac isso não é necessário, mas você ainda vai precisar carregar a configuração filmic.</p>
<p>Na aba Scene, você deve reconfigurar o &ldquo;Color Management&rdquo; assim:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/636/Screenshot_from_2017-06-21_16-31-05.png" alt="Color Management"  loading="lazy" /></p>
<p>Em seguida, configure o Cycles. Se você está no Linux com o Optimus instalado corretamente como expliquei antes, a opção CUDA deve aparecer nas preferências do usuário:</p>
<p><a href="https://web.archive.org/web/20181118204134/https://docs.blender.org/manual/en/dev/preferences/system.html"target="_blank" rel="noopener"><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/637/Screenshot_from_2017-06-21_16-43-29.png" alt="Preferências do sistema"  loading="lazy" /></a></p>
<p>Na aba Render, você deve ter algo assim:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/638/Screenshot_from_2017-06-21_16-45-32.png" alt="Configuração de Render"  loading="lazy" /></p>
<p>Na seção Dimensions, você vai ver que está configurado para Full HD (1920x1080 px) mas com apenas 50% (ou seja, vai renderizar metade do tamanho). Aumente para 100%. Se quiser um shot em 4K, ajuste as dimensões para 3840x2160 px. O 4K torna a renderização 4 vezes mais lenta que o 1080p, obviamente.</p>
<p>Na seção Performance há 2 campos para &ldquo;Tile size&rdquo;. O Blender vai dividir sua imagem em tiles, e cada tile renderiza em paralelo num core de CPU ou GPU disponível. Meu notebook tem 8 cores de CPU, então 64 é um bom tamanho, porque vai renderizar 8 tiles de 64 pixels cada em paralelo.</p>
<p>Quanto menos cores você tiver, maior deve ser o tile. Para minha GPU NVIDIA, só tenho 2 cores disponíveis (e não muita memória de vídeo!), então vale aumentar os dois campos para 512. Na GPU ela só renderiza 2 tiles por vez, e tiles maiores otimizam a renderização.</p>
<p>Como você provavelmente já adivinhou, o Blender Guru tem um tutorial muito útil: <a href="https://www.youtube.com/watch?v=8gSyEpt4-60&amp;t=204s"target="_blank" rel="noopener">&ldquo;18 ways to speed up Cycles Rendering&rdquo;</a>.</p>
<p>Isso cobre o básico das configurações padrão.</p>
<h3>Você Precisa Pensar como Fotógrafo!<span class="hx:absolute hx:-mt-20" id="você-precisa-pensar-como-fotógrafo"></span>
    <a href="#voc%c3%aa-precisa-pensar-como-fot%c3%b3grafo" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Você vai querer assistir MUITOS tutoriais online, porque realmente não é óbvio como usar melhor as ferramentas. Outro canal que gosto bastante é o <a href="https://www.youtube.com/watch?v=lY6KPrc4uMw&amp;list=PLda3VoSoc_TR7X7wfblBGiRz-bvhKpGkS"target="_blank" rel="noopener">BornCG</a> e o <a href="https://www.youtube.com/channel/UCCxay0KiyLlawfgoZ2mVnNQ"target="_blank" rel="noopener">CG Masters</a>. Pegue alguns vídeos deles para ter uma visão mais aprofundada de ferramentas específicas, técnicas de modelagem e por aí vai.</p>
<p>E de verdade: você precisa praticar o máximo possível enquanto estuda. Uma área importante é a Fotografia. Isso é o que você vê quando seleciona a aba Camera:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/639/Screenshot_from_2017-06-21_16-55-09.png" alt="Configurações de câmera"  loading="lazy" /></p>
<p>Tem muita customização disponível aqui. Por exemplo: Focal length: 35.00, que é o que os fotógrafos conhecem como lente de 35mm, uma boa escolha padrão. Você pode usar grande angular de 200mm ou 300mm também, e shots internos, em salas pequenas, podem usar 24mm ou até 18mm. O artigo <a href="http://www.cambridgeincolour.com/tutorials/camera-lenses.htm"target="_blank" rel="noopener">&ldquo;Understanding Color Lenses&rdquo;</a> cobre o básico.</p>
<p>Depois, você precisa entender &ldquo;Profundidade de Campo&rdquo; (Depth of Field). Isso pode ser feito nessa configuração antes de renderizar, ou simulado após a renderização (se você escolheu separar o render em camadas), no <a href="https://web.archive.org/web/20160308021547/http://wiki.blender.org/index.php/Doc:2.6/Tutorials/Composite_Nodes/Setups/Depth_Of_Field"target="_blank" rel="noopener">Compositor</a>.</p>
<p>Falando nisso, outra forma de controlar a Profundidade de Campo é reconfigurar o &ldquo;f-stop&rdquo;, que é a medida de exposição, ou abertura. O padrão é &ldquo;128.0&rdquo;, ou seja, &ldquo;f/128&rdquo;. Como referência, a câmera do iPhone 7s tem abertura f/2.2. Novamente, vale estudar mais sobre isso começando pelo artigo <a href="https://www.bhphotovideo.com/explora/photography/tips-and-solutions/what%E2%80%99s-best-f-stop"target="_blank" rel="noopener">&ldquo;What&rsquo;s the Best F-Stop?&rdquo;</a></p>
<p>Tirar uma foto (ou renderizar um still, no nosso caso) é muito mais do que simplesmente apontar e clicar. Você também precisa se preocupar com técnicas adequadas de composição, como a <a href="http://www.photographymad.com/pages/view/rule-of-thirds"target="_blank" rel="noopener">Regra dos Terços</a>, a <a href="http://www.makeuseof.com/tag/golden-ratio-photography/"target="_blank" rel="noopener">proporção áurea</a>, e por aí vai. Você pode configurar isso no combo box &ldquo;Composition Guides&rdquo; mostrado acima.</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/641/3911f4657078a19b4f3677a304e7451d.jpg" alt="Composição com Proporção Áurea"  loading="lazy" /></p>
<p>Vale muito a pena se aprofundar no campo da Fotografia para melhorar o resultado final dos renders. Sou um amador e é muito empolgante conseguir aplicar técnicas do mundo real na renderização 3D.</p>
<h3>Design de Materiais e Physically-Based Render (PBR)<span class="hx:absolute hx:-mt-20" id="design-de-materiais-e-physically-based-render-pbr"></span>
    <a href="#design-de-materiais-e-physically-based-render-pbr" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O padrão ouro em modelagem e renderização 3D é certamente o <a href="https://renderman.pixar.com/view/renderman"target="_blank" rel="noopener">Disney/Pixar RenderMan</a>. Todos os filmes premiados da Pixar foram feitos com ele.</p>
<p>Mas o Blender aprende rápido. Todo paper da Pixar eventualmente vira parte do próprio Blender. O design de materiais, por exemplo, sempre foi bastante trabalhoso na ferramenta. Fiz alguns dos tutoriais eu mesmo, e criar materiais com as características corretas do mundo real, como Fresnel adequado e a distinção certa entre dielétrico e metálico, era um desafio.</p>
<p>Se você assinou o canal do Blender Guru, assista os tutoriais <a href="https://www.youtube.com/watch?v=V3wghbZ-Vh4&amp;t=2668s"target="_blank" rel="noopener">&ldquo;How to Make Photorealistic PBR Materials - Part 1&rdquo;</a> e <a href="https://www.youtube.com/watch?v=m1PkSViBi-M"target="_blank" rel="noopener">&ldquo;Part 2&rdquo;</a>. E você vai acabar com essa configuração complicada de Nodes para materiais PBR:</p>
<p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/640/Screenshot_from_2017-06-21_17-25-46.png" alt="Materiais PBR - Node Editor"  loading="lazy" /></p>
<p>E o Blender Guru acabou de postar um novo vídeo apresentando um recurso do próximo Blender 2.79 (ainda no 2.78 na época): a implementação do paper <a href="https://disney-animation.s3.amazonaws.com/library/s2012_pbs_disney_brdf_notes_v2.pdf"target="_blank" rel="noopener">&ldquo;Physically-Based Shader at Disney&rdquo;</a> como um novo shader nativo e otimizado chamado &ldquo;Principled Shader&rdquo;. É literalmente o <a href="https://www.youtube.com/watch?v=4H5W6C_Mbck"target="_blank" rel="noopener">Shader Definitivo</a>, porque torna a criação e personalização de materiais realistas <strong>muito</strong> mais fácil, além de ser compatível com Renderman e Substance.</p>
<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Ainda sou iniciante no Blender, tem uma estrada muito longa pela frente. Mas é um ambiente muito empolgante e estou aprendendo coisas novas e úteis o tempo todo. Todo mundo deveria experimentar!</p>
<p>Com o tempo, espero encontrar espaço para integrar o fluxo de trabalho do Blender com outras ferramentas como Unreal Engine ou Unity3D para criar coisas interativas também.</p>
<p>Esse post não é de forma alguma um tutorial completo ou referência definitiva. A ideia foi destacar algumas coisas que não são imediatamente óbvias quando você começa com o Blender e que podem te dar uma noção mais ampla do que ele é capaz de fazer.</p>
<p>Se quiser se aprofundar de verdade nas configurações, assista esse vídeo de setup do CG Master:</p>


<div class="embed-container">
  <iframe
    src="https://www.youtube.com/embed/-_BZasG_UDA"
    title="YouTube video player"
    frameborder="0"
    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
    referrerpolicy="strict-origin-when-cross-origin"
    allowfullscreen>
  </iframe>
</div>

]]></content:encoded><category>tutoriais</category><category>aprendizado</category><category>audio-e-video</category><category>off-topic</category></item><item><title>Sua aplicação Rails está pronta para Produção?</title><link>https://akitaonrails.github.io/2016/03/22/sua-aplicacao-rails-esta-pronta-para-producao/</link><guid isPermaLink="true">https://akitaonrails.github.io/2016/03/22/sua-aplicacao-rails-esta-pronta-para-producao/</guid><pubDate>Tue, 22 Mar 2016 15:11:00 -0300</pubDate><description>&lt;p&gt;Isto aqui é um checklist. Siga estas instruções se você quiser ter um pouco de paz de espírito depois de colocar uma aplicação em produção. Se você tem experiência, contribua nos comentários abaixo. Mas se você é iniciante, preste muita atenção.&lt;/p&gt;
&lt;h3&gt;Onde fazer o Deploy? TL;DR: Heroku&lt;span class="hx:absolute hx:-mt-20" id="onde-fazer-o-deploy-tldr-heroku"&gt;&lt;/span&gt;
&lt;a href="#onde-fazer-o-deploy-tldr-heroku" class="subheading-anchor" aria-label="Permalink for this section"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Muita gente vai mirar primeiro nas opções baratas. Normalmente instâncias EC2 micro ou pequenos droplets do Digital Ocean. A menos que você tenha consciência de que &lt;strong&gt;precisa&lt;/strong&gt; monitorar e atualizar essas instâncias o tempo todo, &lt;strong&gt;NÃO&lt;/strong&gt; faça isso.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Isto aqui é um checklist. Siga estas instruções se você quiser ter um pouco de paz de espírito depois de colocar uma aplicação em produção. Se você tem experiência, contribua nos comentários abaixo. Mas se você é iniciante, preste muita atenção.</p>
<h3>Onde fazer o Deploy? TL;DR: Heroku<span class="hx:absolute hx:-mt-20" id="onde-fazer-o-deploy-tldr-heroku"></span>
    <a href="#onde-fazer-o-deploy-tldr-heroku" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Muita gente vai mirar primeiro nas opções baratas. Normalmente instâncias EC2 micro ou pequenos droplets do Digital Ocean. A menos que você tenha consciência de que <strong>precisa</strong> monitorar e atualizar essas instâncias o tempo todo, <strong>NÃO</strong> faça isso.</p>
<p>Em vez disso, escolha uma solução PaaS como o Heroku. É óbvio. Software em produção é coisa séria e contínua. Só pelo lado da segurança, você não deveria fazer isso sozinho a menos que saiba como hardenizar uma distribuição de SO. Por exemplo, a maioria das pessoas nem sabe o que é o <a href="http://www.fail2ban.org/wiki/index.php/Main_Page"target="_blank" rel="noopener">fail2ban</a>. Não assuma que, só porque você usa SSH com par de chaves, está seguro. Você não está.</p>
<p>Por exemplo, você sabia que a gente acabou de passar por uma <a href="https://blog.heroku.com/archives/2016/3/21/patching-glibc-security-hole"target="_blank" rel="noopener">crise de segurança da glibc</a>? Se você não sabia e há tempos não toca nos seus servidores de produção para atualizar todos os componentes, você está aberto, completamente aberto. Tive vários clientes que contrataram freelancers para implementar e fazer deploy das aplicações e depois ficaram sem ninguém olhando aquilo. Todos vocês estão inseguros.</p>
<p>O Heroku também não é garantia, porque as bibliotecas das aplicações ficam velhas, vulnerabilidades são descobertas, e você precisa atualizá-las. O Rails tem um <a href="https://groups.google.com/forum/#!forum/rubyonrails-security"target="_blank" rel="noopener">time de segurança ativo</a> lançando patches o tempo inteiro. Se você não anda atualizando, está inseguro.</p>
<p>Mas suporte 24x7 é caro. Se você tem infraestrutura própria ou usa uma plataforma IaaS como AWS EC2, você precisa de pessoal 24x7, ou seja, no mínimo 2 administradores de sistema/devops em tempo integral cuidando das coisas. A alternativa é usar um PaaS que pelo menos mantém essas higienes básicas em dia, mais um desenvolvedor part-time só para manter as suas aplicações atualizadas.</p>
<p>Se você não fizer isso, está expondo seu negócio a uma responsabilidade enorme. E se você é desenvolvedor, faça a coisa certa: avise seus clientes sobre esse fato e deixe-os tomar uma decisão informada. Eles podem optar por continuar no barato, mas pelo menos sabem o que isso realmente significa.</p>
<h3>Qual web server? TL;DR: Passenger ou Puma<span class="hx:absolute hx:-mt-20" id="qual-web-server-tldr-passenger-ou-puma"></span>
    <a href="#qual-web-server-tldr-passenger-ou-puma" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>A maioria das aplicações Rails não tem nem as bibliotecas básicas instaladas para funcionar direito.</p>
<p>Já vi gente rodando aplicação em produção simplesmente subindo o <tt>rails server</tt> e mais nada! Isso roda o Webrick, o web server básico, em produção! Você tem que escolher entre Puma, Unicorn ou Passenger. O Unicorn, na verdade, não protege sua aplicação contra clientes lentos, então evite-o se puder.</p>
<p>Se estiver na dúvida, <a href="https://www.phusionpassenger.com/library/walkthroughs/deploy/ruby/heroku/standalone/oss/deploy_app_main.html"target="_blank" rel="noopener">instale o Passenger</a> e vá tomar um café.</p>
<p>A escolha mais comum hoje em dia é <a href="https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server"target="_blank" rel="noopener">usar Puma</a>. Mas se você está rodando no Heroku ou usando instâncias pequenas, tenha em mente o consumo de RAM. Uma aplicação Rails normal consome no mínimo 150Mb a 200Mb ou mais, dependendo do tamanho do seu app. O menor dyno do Heroku tem só 512Mb de RAM, o que significa que você não deve configurar Puma ou Unicorn com mais que 2 instâncias. Eu recomendaria ir de Dyno 2X (1GB) e ter no máximo 3 instâncias (ou seja, 3 requisições concorrentes possíveis sendo processadas ao mesmo tempo).</p>
<p>É comum sua aplicação ir aumentando o consumo de RAM ao longo do tempo, e a cada par de dias você ver um R14 acontecendo. Aí o dyno reinicia e começa a funcionar melhor por um tempo, até bater no teto de novo.</p>
<p>Se você não sabe por que isso acontece e é algo infrequente, dá para usar temporariamente o <a href="https://github.com/schneems/puma_worker_killer"target="_blank" rel="noopener">Puma Worker Killer</a> - se você estiver usando Puma, claro - para ficar monitorando suas instâncias até que cheguem a um certo teto mais baixo e reciclem automaticamente antes que seus usuários vejam os erros R14.</p>
<p>Você também já deve ter ouvido falar do <a href="http://tmm1.net/ruby21-oobgc"target="_blank" rel="noopener">Out-of-Band GC</a>. Mas <a href="https://github.com/puma/puma/issues/450"target="_blank" rel="noopener">fique avisado</a> que o Puma em modo threaded não funciona bem com isso. Se você usa Unicorn, então use sem medo.</p>
<h3>Meça, Meça, Meça. TL;DR: New Relic<span class="hx:absolute hx:-mt-20" id="meça-meça-meça-tldr-new-relic"></span>
    <a href="#me%c3%a7a-me%c3%a7a-me%c3%a7a-tldr-new-relic" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><img src="https://akitaonrails.s3.amazonaws.com/assets/image_asset/image/535/big_heroku-metrics.png" alt="Heroku Metrics"  loading="lazy" /></p>
<p>Você deve <strong>sempre</strong> monitorar sua aplicação. Seja com software instalado por conta própria como Nagios, Munin, Zabbix, Zenoss, Ganglia etc, ou no mínimo com as métricas que o próprio Heroku fornece, como na imagem acima.</p>
<p>Se suas instâncias estão se comportando de forma estranha, dê uma olhada nos avisos. Um conjunto frequente de erros <a href="https://devcenter.heroku.com/articles/ruby-memory-use"target="_blank" rel="noopener">R14</a> significa que o consumo de memória da sua app está crescendo ao longo do tempo e estourando o limite de RAM disponível na instância (provável vazamento de memória). No mínimo, isso significa que você deve diminuir a concorrência/workers no seu Puma/Unicorn e também aumentar o número de dynos para compensar, se tiver tráfego suficiente para justificar. Mas essa é uma ação orientada a métricas que você pode tomar porque tem informação para uma decisão consciente.</p>
<p>Você também deve estar armazenando e indexando seus logs usando software instalado como Greylog2 ou Logstash, ou uma alternativa SaaS como o <a href="https://elements.heroku.com/addons/papertrail"target="_blank" rel="noopener">Papertrail</a>. Existem várias razões pelas quais uma aplicação pode estar vazando, ou pelas quais tudo parece estar bem nas métricas mas você está tendo erros aleatórios. Sempre consulte os logs para ter respostas.</p>
<p>Melhor ainda, <a href="https://docs.newrelic.com/docs/agents/ruby-agent/miscellaneous/ruby-agent-heroku"target="_blank" rel="noopener">instale o New Relic APM</a>. Você terá métricas, alertas automatizados, medição de erros e diagnósticos realmente profundos para ver exatamente que linha do seu código fonte é a culpada pela performance ruim, pelos erros frequentes ou pelo comportamento errático. Se estiver em dúvida, instale logo, o plano básico é grátis mesmo.</p>
<h3>Algumas Dependências do Rails<span class="hx:absolute hx:-mt-20" id="algumas-dependências-do-rails"></span>
    <a href="#algumas-depend%c3%aancias-do-rails" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Você <strong>NÃO</strong> deveria estar usando Rails abaixo da versão 4.0 a essa altura. O 3.2 ainda é bastante usado, mas o suporte vai acabar logo. Não use o Rails 5.0 agora a menos que tenha experiência suficiente para saber o que fazer. Qualquer coisa entre 4.0 e 4.2 deve estar ok, mas fique atento aos minor releases que corrigem vulnerabilidades de segurança.</p>
<p>Você <strong>NÃO</strong> deveria estar usando Ruby abaixo da versão 2.2 a essa altura. As atualizações de segurança para o 2.0 acabaram em fevereiro de 2016. O 2.1 não foi um release estável bom, mas o 2.2 e o 2.3 estão muito bons e você deveria estar usando eles.</p>
<p>Então a combinação Ruby 2.3.0 e Rails 4.2.5.2 (a partir de março de 2016) é a escolha ideal para a melhor estabilidade, segurança e suporte do ecossistema.</p>
<p>Você deveria ter, no mínimo, as seguintes dependências instaladas por padrão:</p>
<ul>
<li><a href="https://github.com/heroku/rails_12factor"target="_blank" rel="noopener">Rails 12 Factor gem</a></li>
<li><a href="http://rtomayko.github.io/rack-cache/"target="_blank" rel="noopener">Rack-Cache middleware</a>
<ul>
<li><a href="https://devcenter.heroku.com/articles/memcachier#rails-3-and-4"target="_blank" rel="noopener">Gem Dalli com Memcached ou Memcachier</a> - mesmo se você não estiver usando fragment caching ou AR caching, isso ainda é útil pelo menos para a <a href="https://devcenter.heroku.com/articles/rack-cache-memcached-rails31"target="_blank" rel="noopener">gem Rack-Cache</a>.</li>
</ul>
</li>
<li><a href="https://github.com/kickstarter/rack-attack"target="_blank" rel="noopener">Rack-Attack</a> - leia como configurar direito!</li>
<li><a href="http://www.sinatrarb.com/rack-protection/"target="_blank" rel="noopener">Rack-Protection</a> - leia como configurar direito e quais proteções você quer ativar, não tudo, mas pelo menos CSRF e XSS.</li>
<li><a href="http://www.schneems.com/blogs/2016-02-18-speeding-up-sprockets/"target="_blank" rel="noopener">Sprockets 3.3+</a> - garanta que está usando a versão do Sprockets que corrige a lentidão e deixa as coisas razoavelmente mais rápidas</li>
</ul>
<h3>Qual Banco de Dados? TL;DR: use Postgresql<span class="hx:absolute hx:-mt-20" id="qual-banco-de-dados-tldr-use-postgresql"></span>
    <a href="#qual-banco-de-dados-tldr-use-postgresql" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p><strong>NÃO</strong> escolha MongoDB ou similar a menos que tenha uma razão muito forte e esteja confiante de que é muito bom no que faz e vai ficar por perto para manter o que colocou em produção. Caso contrário, fique no Postgresql e na gem &ldquo;pg&rdquo;, você não vai se arrepender, nem o seu cliente.</p>
<p>Use instâncias pequenas de Redis (particularmente opções SaaS como o <a href="https://elements.heroku.com/addons/heroku-redis"target="_blank" rel="noopener">Heroku Redis</a>) com o <a href="https://github.com/mperham/sidekiq/wiki/Deployment"target="_blank" rel="noopener">Sidekiq</a>. Você pode optar pelo Sucker Punch, mais leve, mas saiba que ele vai aumentar bastante o consumo de RAM das suas instâncias dependendo do tamanho das filas e dos dados serializados de entrada para os workers. Nem sempre é trivial saber quantos workers Sidekiq você deveria ter, mas <a href="http://manuel.manuelles.nl/sidekiq-heroku-redis-calc/"target="_blank" rel="noopener">esta calculadora</a> pode ajudar.</p>
<p>Existem pelo menos 2 grandes erros que a maioria das pessoas comete usando Sidekiq:</p>
<ul>
<li>Ter workers pesados batendo no seu banco de dados master. Por exemplo, um grande procedimento de analytics buscando grandes conjuntos de dados enquanto seu site principal também tem uso intenso de usuários escrevendo dados o tempo inteiro. Definitivamente adicione um <a href="https://devcenter.heroku.com/articles/heroku-postgres-follower-databases"target="_blank" rel="noopener">banco Follower</a> e aponte os workers Sidekiq (read-only) para lá, para analytics, relatórios e cargas similares.</li>
<li>Buscar grandes datasets em memória e estourar seus dynos de worker. Se você fizer qualquer coisa parecida com &ldquo;YourModel.all.each { |r| &hellip; }&rdquo;, está dando um tiro no próprio pé. O Rails tem formas fáceis de <a href="http://api.rubyonrails.org/classes/ActiveRecord/Batches.html"target="_blank" rel="noopener">buscar pequenos conjuntos em batch</a> para você processar, use-as.</li>
</ul>
<h3>Você está usando um CDN? TL;DR: simplesmente use<span class="hx:absolute hx:-mt-20" id="você-está-usando-um-cdn-tldr-simplesmente-use"></span>
    <a href="#voc%c3%aa-est%c3%a1-usando-um-cdn-tldr-simplesmente-use" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Isso é fácil, use <a href="https://devcenter.heroku.com/articles/using-amazon-cloudfront-cdn"target="_blank" rel="noopener">AWS CloudFront</a> ou <a href="https://devcenter.heroku.com/articles/fastly"target="_blank" rel="noopener">Fastly</a> ou outra coisa, mas <strong>USE</strong> um CDN. É óbvio.</p>
<p>Caso contrário, seus assets vão ser servidos pelas suas instâncias de web dyno, que já são pequenas. E isso é um trabalho pesado que é melhor servido por um CDN muito mais rápido. Seus usuários vão perceber ordens de magnitude em usabilidade só mudando algumas linhas de configuração no seu código.</p>
<p>Se você implementou suas views, templates e stylesheets do Rails usando os helpers &ldquo;image_tag&rdquo; e &ldquo;asset_path&rdquo;, isso é automático. Faça logo.</p>
<p>E já que estamos falando de assets, se você por acaso tem upload de imagens e está usando o jeito simples de postar um formulário multipart direto para a sua aplicação Rails, vai ter problemas, porque o Heroku impõe - corretamente - um timeout máximo de 30 segundos. Se você tem muitos usuários subindo muitas imagens de alta resolução e tamanho pesado, eles vão ver cada vez mais timeouts.</p>
<p>Use a <a href="http://cloudinary.com/documentation/rails_additional_topics"target="_blank" rel="noopener">gem Attachinary</a> para fazer upload direto via Javascript, do navegador para o serviço Cloudinary, contornando completamente sua aplicação Rails, que vai apenas receber o ID quando o upload terminar. De novo, tem um plano grátis bem generoso e é tão simples de implementar que você precisa fazer isso já.</p>
<h3>Você demitiu seu desenvolvedor, e agora? TL;DR: revogue os acessos!<span class="hx:absolute hx:-mt-20" id="você-demitiu-seu-desenvolvedor-e-agora-tldr-revogue-os-acessos"></span>
    <a href="#voc%c3%aa-demitiu-seu-desenvolvedor-e-agora-tldr-revogue-os-acessos" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Isso acontece muito: uma empresa pequena precisa cortar custos e demitir desenvolvedores.</p>
<p>Garanta que você revogue os acessos. Na AWS isso é bem mais convoluto se você não conhece bem a ferramenta de autorização IAM deles. Ou pior: se eles têm as chaves privadas (arquivos .pem) para suas instâncias EC2!! Você vai ter que criar novos pares de chaves, desativar as instâncias EC2 antigas e criar novas. Uma puta dor de cabeça.</p>
<p>Você precisa trocar senhas do seu serviço de DNS, da organização no Github/Bitbucket, remover chaves SSH dos arquivos .ssh/authorized_keys em todos os seus servidores etc.</p>
<p>Garanta que sua empresa tem um bom suporte jurídico e faça com que cada colaborador, funcionário ou terceirizado, assine um acordo de confidencialidade, NDA ou non-compete.</p>
<p>Você <strong>PRECISA</strong> ter pelo menos uma pessoa-chave na empresa que tem controle sobre as chaves, senhas e acesso a todo serviço do qual sua empresa depende para funcionar. Caso contrário, não se surpreenda quando sua aplicação ficar fora do ar.</p>
<p>E não se iluda achando que, só porque uma aplicação está online e rodando, vai continuar assim por muito tempo. Vulnerabilidades de segurança estão soltas por aí, é só uma questão de tempo até algum problema derrubar seu serviço, e é melhor você ter alguém para colocá-lo de volta no ar se você mesmo não souber fazer isso.</p>
<h3>Site Grande? Parabéns, e agora?<span class="hx:absolute hx:-mt-20" id="site-grande-parabéns-e-agora"></span>
    <a href="#site-grande-parab%c3%a9ns-e-agora" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>Se seu negócio está prosperando, florescendo, parabéns. Você está naquela porcentagem muito muito pequena que realmente conseguiu.</p>
<p>Em breve sua aplicação novinha em folha vai virar aquele sistema feio e temido chamado <strong>Legacy</strong>. Pior, provavelmente é um daqueles <strong>monolitos</strong> assustadores ou, pior ainda, você deixou um desenvolvedor hipster criar uma arquitetura espaguete cheia de <strong>microserviços</strong> sem controle. Você está condenado de qualquer jeito.</p>
<p>Essa situação exige que você fique com os pés no chão: bala de prata não existe, é o trabalho de sempre.</p>
<p>Você precisa se manter à frente do jogo, e a parte de monitoramento que mencionei tem um papel grande para guiar você pelas várias direções possíveis. Por exemplo, por um curto período de tempo dá para aumentar os planos dos seus serviços SaaS como bancos de dados, caches, filas, e também adicionar mais web dynos paralelos.</p>
<p>Isso vai segurar a onda por um tempo (suas métricas podem ajudar a estimar por quanto), mas agora você precisa montar um plano para otimizar o que tem. E não, reescrever tudo é a última coisa que você quer fazer, a menos que pretenda não ter vida pelos próximos dois anos.</p>
<p>Confie em Pareto: na vida real, 80% dos principais problemas podem ser resolvidos com 20% do esforço. A grande dica: abra o New Relic - agora carregado com dados reais de produção - e olhe os Top piores performers e comece por aí. O primeiro que você resolver (seja uma feia query N+1 no banco, uma integração lenta com web service), vai te dar muito espaço para respirar.</p>
<h3>Conclusão<span class="hx:absolute hx:-mt-20" id="conclusão"></span>
    <a href="#conclus%c3%a3o" class="subheading-anchor" aria-label="Permalink for this section"></a></h3><p>O que eu acabei de dizer é só a ponta do iceberg em termos de otimização de performance Rails. E posso te garantir que existe muito espaço para melhorar sem precisar recorrer a reescrever tudo na próxima linguagem da moda. Recomendo que você compre o <a href="https://www.railsspeed.com/"target="_blank" rel="noopener">&ldquo;The Complete Guide to Rails Performance&rdquo;</a> do Nate Berkopec para realmente aprender o terreno.</p>
<p>Esta é uma compilação muito rápida que despejei da cabeça, mas tem muito mais coisa que podemos considerar. Mande perguntas na seção de comentários e outros itens importantes que eu posso ter deixado passar.</p>
]]></content:encoded><category>tutoriais</category><category>devops</category><category>rails</category></item></channel></rss>