<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Edimar Cardoso</title>
    <description>The latest articles on Forem by Edimar Cardoso (@edimarlnx).</description>
    <link>https://forem.com/edimarlnx</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2136912%2F890717ff-d12e-4154-bb69-e751dee8d90d.jpg</url>
      <title>Forem: Edimar Cardoso</title>
      <link>https://forem.com/edimarlnx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/edimarlnx"/>
    <language>en</language>
    <item>
      <title>Configurando Caddy: Arquivos Estáticos e Autenticação Básica atrás de um Load Balancer</title>
      <dc:creator>Edimar Cardoso</dc:creator>
      <pubDate>Thu, 03 Oct 2024 19:09:11 +0000</pubDate>
      <link>https://forem.com/quave/configurando-caddy-arquivos-estaticos-e-autenticacao-basica-atras-de-um-load-balancer-3hn9</link>
      <guid>https://forem.com/quave/configurando-caddy-arquivos-estaticos-e-autenticacao-basica-atras-de-um-load-balancer-3hn9</guid>
      <description>&lt;p&gt;O &lt;a href="https://caddyserver.com/" rel="noopener noreferrer"&gt;Caddy server&lt;/a&gt; tem se mostrado um projeto muito promissor. Além de ser rápido, tem muitas funcionalidades "built-in" que facilitam muito a configuração. Um dos exemplos é a emissão automática de certificados SSL. Além disso, é muito fácil configurar um serviço utilizando ele.&lt;/p&gt;

&lt;p&gt;Esta semana, precisei criar um serviço para publicar arquivos estáticos. Ao tentar fazer isto com a configuração padrão dele, notei que não seria possível usar a configuração padrão por ter um cenário um pouco diferente.&lt;/p&gt;

&lt;p&gt;No meu cenário, o serviço iria rodar atrás de um load balancer na &lt;a href="https://www.zcloud.ws" rel="noopener noreferrer"&gt;zCloud&lt;/a&gt; que já é responsável por gerar os certificados e fazer a terminação do TLS/SSL. A configuração padrão gerou problemas pois não deveria gerar certificado SSL, apenas servir os arquivos na porta HTTP padrão.&lt;/p&gt;

&lt;p&gt;Além disso, havia outra particularidade: precisava de um endpoint de health check com acesso público, enquanto todo o restante precisava ser bloqueado utilizando autenticação padrão "Basic Auth".&lt;/p&gt;

&lt;p&gt;A seguir, o arquivo utilizado para fazer isto de uma forma bem simples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  auto_https off
}
:8080 {
    route /_health* {
        respond 200
    }
    route {
        basic_auth / {
            USER_NAME HASH_PASSWORD
        }
        root * /var/www/static
        file_server
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Explicando um pouco sobre a configuração:&lt;/p&gt;

&lt;h2&gt;
  
  
  Desabilitando SSL
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auto_https off
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Desabilita a emissão automática de certificado e desabilita o redirecionamento para HTTPS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Porta
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:8080 {
# ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Porta que será aberta para receber conexões.&lt;/p&gt;

&lt;h2&gt;
  
  
  Health check
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    route /_health* {
        respond 200
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rota para acesso ao health check com acesso público.&lt;/p&gt;

&lt;h2&gt;
  
  
  Arquivos estáticos com autenticação
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    route {
        basic_auth / {
            USER_NAME HASH_PASSWORD
        }
        root * /var/www/static
        file_server
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta parte da configuração irá receber todo restante das requests, interceptar as requisições e forçar um login, caso o usuário não esteja logado, e servir os arquivos do diretório &lt;code&gt;/var/www/static&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;O valor &lt;code&gt;USER_NAME&lt;/code&gt; é o nome do usuário que será usado para autenticar.&lt;br&gt;
O valor &lt;code&gt;HASH_PASSWORD&lt;/code&gt; é o hash gerado pelo CLI do Caddy com base na senha definida. Para gerar este valor, basta seguir as instruções na documentação oficial &lt;a href="https://caddyserver.com/docs/caddyfile/directives/basic_auth" rel="noopener noreferrer"&gt;basic_auth&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Esta configuração do Caddy Server oferece uma solução elegante para servir arquivos estáticos com autenticação, ao mesmo tempo que permite um endpoint de health check público. A flexibilidade e simplicidade do Caddy tornam-no uma excelente escolha para diversos cenários de deploy.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Using Caddy to Serve Static Files Behind a Load Balancer</title>
      <dc:creator>Edimar Cardoso</dc:creator>
      <pubDate>Thu, 03 Oct 2024 19:07:34 +0000</pubDate>
      <link>https://forem.com/quave/using-caddy-to-serve-static-files-behind-a-load-balancer-h2l</link>
      <guid>https://forem.com/quave/using-caddy-to-serve-static-files-behind-a-load-balancer-h2l</guid>
      <description>&lt;p&gt;&lt;a href="https://caddyserver.com/" rel="noopener noreferrer"&gt;Caddy server&lt;/a&gt; has proven to be a very promising project. Not only is it fast, but it also has many built-in features that greatly simplify configuration. One example is the automatic issuance of SSL certificates. Moreover, it's very easy to set up a service using Caddy.&lt;/p&gt;

&lt;p&gt;This week, I needed to create a service to publish static files. When trying to do this with Caddy's default configuration, I noticed it wouldn't be possible due to a slightly different scenario.&lt;/p&gt;

&lt;p&gt;In my scenario, the service would run behind a load balancer at &lt;a href="https://www.zcloud.ws" rel="noopener noreferrer"&gt;zCloud&lt;/a&gt; that's already responsible for generating certificates and handling TLS/SSL termination. The default configuration caused issues because it shouldn't generate an SSL certificate, but only serve files on the default HTTP port.&lt;/p&gt;

&lt;p&gt;Additionally, there was another particularity: I needed a health check endpoint with public access, while everything else needed to be blocked using standard "Basic Auth" authentication.&lt;/p&gt;

&lt;p&gt;Here's the file used to accomplish this in a simple way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  auto_https off
}
:8080 {
    route /_health* {
        respond 200
    }
    route {
        basic_auth / {
            USER_NAME HASH_PASSWORD
        }
        root * /var/www/static
        file_server
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down the configuration:&lt;/p&gt;

&lt;h2&gt;
  
  
  Disabling SSL
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auto_https off
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This disables automatic certificate issuance and disables redirection to HTTPS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Port
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:8080 {
# ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This specifies the port that will be opened to receive connections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Health check
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    route /_health* {
        respond 200
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This route provides public access to the health check endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Static files with authentication
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    route {
        basic_auth / {
            USER_NAME HASH_PASSWORD
        }
        root * /var/www/static
        file_server
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This part of the configuration will receive all other requests, intercept them and force a login if the user is not logged in, and serve files from the &lt;code&gt;/var/www/static&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;USER_NAME&lt;/code&gt; value is the username that will be used for authentication.&lt;br&gt;
The &lt;code&gt;HASH_PASSWORD&lt;/code&gt; value is the hash generated by the Caddy CLI based on the defined password. To generate this value, simply follow the instructions in the official &lt;a href="https://caddyserver.com/docs/caddyfile/directives/basic_auth" rel="noopener noreferrer"&gt;basic_auth documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This Caddy Server configuration offers an elegant solution for serving static files with authentication while allowing a public health check endpoint. Caddy's flexibility and simplicity make it an excellent choice for various deployment scenarios.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Migrating from shell script to "Bun script"</title>
      <dc:creator>Edimar Cardoso</dc:creator>
      <pubDate>Fri, 27 Sep 2024 21:58:59 +0000</pubDate>
      <link>https://forem.com/edimarlnx/migrating-from-shell-script-to-bun-script-4pnj</link>
      <guid>https://forem.com/edimarlnx/migrating-from-shell-script-to-bun-script-4pnj</guid>
      <description>&lt;p&gt;When working on a project focused on process automation and infrastructure at &lt;a href="https://www.zcloud.ws" rel="noopener noreferrer"&gt;zCloud&lt;/a&gt;, we frequently encounter the need to create multiple functions to perform validations and common processes. Everything works fine when using only one operating system, but the situation becomes complicated when more than one system is involved.&lt;/p&gt;

&lt;p&gt;In our case, most of the development happens on Linux, but we also need to ensure compatibility with macOS. This often results in code incompatibilities.&lt;/p&gt;

&lt;p&gt;To address this issue, we are migrating our shell script functions to JavaScript files, using &lt;a href="https://bun.sh/" rel="noopener noreferrer"&gt;Bun&lt;/a&gt; as the interpreter. We chose Bun because it provides a simple way to run commands like a shell through its &lt;a href="https://bun.sh/docs/runtime/shell" rel="noopener noreferrer"&gt;Shell API&lt;/a&gt; functionality.&lt;/p&gt;

&lt;p&gt;Below is an example of a function we use to check for any code changes before applying infrastructure modifications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shell script code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;function &lt;/span&gt;zc_check_pristine_git&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ZC_CURRENT_ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"staging"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ZC_CURRENT_ENV&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      return &lt;/span&gt;0
    &lt;span class="k"&gt;fi

    &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;not_pristine&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;modified_files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;

    &lt;span class="c"&gt;# Check for staged but uncommitted changes&lt;/span&gt;
    &lt;span class="nv"&gt;staged_changes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; &lt;span class="nt"&gt;--cached&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$staged_changes&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;not_pristine&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
        modified_files+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Staged changes:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="nv"&gt;$staged_changes&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;

    &lt;span class="c"&gt;# Check for unstaged changes&lt;/span&gt;
    &lt;span class="nv"&gt;unstaged_changes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$unstaged_changes&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;not_pristine&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
        modified_files+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Unstaged changes:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="nv"&gt;$unstaged_changes&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;

    &lt;span class="c"&gt;# Check for untracked files&lt;/span&gt;
    &lt;span class="nv"&gt;untracked_files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git ls-files &lt;span class="nt"&gt;--others&lt;/span&gt; &lt;span class="nt"&gt;--exclude-standard&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$untracked_files&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;not_pristine&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
        modified_files+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Untracked files:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="nv"&gt;$untracked_files&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;

    &lt;span class="c"&gt;# Check if the current branch is ahead of the remote&lt;/span&gt;
    &lt;span class="nv"&gt;ahead_commits&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git log @&lt;span class="o"&gt;{&lt;/span&gt;u&lt;span class="o"&gt;}&lt;/span&gt;.. &lt;span class="nt"&gt;--oneline&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ahead_commits&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;not_pristine&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
        modified_files+&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Commits ahead of the remote:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="nv"&gt;$ahead_commits&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi

    if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$not_pristine&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 1 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$modified_files&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;1
    &lt;span class="k"&gt;fi

    return &lt;/span&gt;0
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To convert this code to JavaScript, we created a file named &lt;code&gt;zc_check_pristine_git&lt;/code&gt; in the project’s &lt;code&gt;bin&lt;/code&gt; directory (which is already in the PATH) with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cp"&gt;#!/usr/bin/env bun
&lt;/span&gt;&lt;span class="c1"&gt;// @language JavaScript&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;checkPristineGit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../js/helpers/helpers.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;checkPristineGit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;currentEnv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ZC_CURRENT_ENV&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We used the &lt;a href="https://en.wikipedia.org/wiki/Shebang_(Unix)" rel="noopener noreferrer"&gt;shebang&lt;/a&gt; &lt;code&gt;#!/usr/bin/env bun&lt;/code&gt; to indicate that we are using &lt;a href="https://bun.sh/" rel="noopener noreferrer"&gt;Bun&lt;/a&gt; as the interpreter.&lt;/p&gt;

&lt;p&gt;We added the comment &lt;code&gt;// @language JavaScript&lt;/code&gt; so that the IDE recognizes the file as JavaScript (we mainly use &lt;a href="https://www.jetbrains.com/" rel="noopener noreferrer"&gt;Jetbrains&lt;/a&gt; tools).&lt;/p&gt;

&lt;p&gt;Then, we imported the function that will actually be executed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation of the function converted from shell to JavaScript:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkPristineGit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;currentEnv&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;exitOnError&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;notEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentEnv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;currentEnv is required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;staging&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentEnv&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;notPristine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;modifiedFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Check for staged but uncommitted changes&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stagedChanges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git diff --name-only --cached`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stagedChanges&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;notPristine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;modifiedFiles&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`Staged changes:\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stagedChanges&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Check for unstaged changes&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;unstagedChanges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git diff --name-only`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;unstagedChanges&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;notPristine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;modifiedFiles&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`Unstaged changes:\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;unstagedChanges&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Check for untracked files&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;untrackedFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git ls-files --others --exclude-standard`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;untrackedFiles&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;notPristine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;modifiedFiles&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`Untracked files:\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;untrackedFiles&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Check if the current branch is ahead of the remote&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aheadCommits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git log @{u}.. --oneline`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aheadCommits&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;notPristine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;modifiedFiles&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;`Commits ahead of the remote:\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;aheadCommits&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;notPristine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error: You can only apply changes in production environments if the repository is in a pristine state.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;modifiedFiles&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, we have standardized JavaScript code that will be executed like a shell script.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;There are calls to functions (&lt;code&gt;exitOnError&lt;/code&gt;, &lt;code&gt;notEmpty&lt;/code&gt;) that are not implemented in the provided example.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>bunjs</category>
      <category>devops</category>
      <category>shell</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
