<?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>The Ops Community ⚙️: Axel Navarro</title>
    <description>The latest articles on The Ops Community ⚙️ by Axel Navarro (@navarroaxel).</description>
    <link>https://community.ops.io/navarroaxel</link>
    <image>
      <url>https://community.ops.io/images/doDT4L_0dK4HG4F2bZDWVPD1nVL1E0EOW5h3-kDoNXE/rs:fill:90:90/g:sm/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkub3BzLmlv/L3JlbW90ZWltYWdl/cy91cGxvYWRzL3Vz/ZXIvcHJvZmlsZV9p/bWFnZS8xMDcvZGMw/YWE4YTItNGRkZC00/N2UyLThhYWItNjgz/NjdlZTVjOGFiLmpw/ZWc</url>
      <title>The Ops Community ⚙️: Axel Navarro</title>
      <link>https://community.ops.io/navarroaxel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://community.ops.io/feed/navarroaxel"/>
    <language>en</language>
    <item>
      <title>Connecting through OpenVPN with deprecated ciphers, using Docker</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Mon, 14 Nov 2022 13:43:50 +0000</pubDate>
      <link>https://community.ops.io/navarroaxel/connecting-through-openvpn-with-deprecated-ciphers-using-docker-488j</link>
      <guid>https://community.ops.io/navarroaxel/connecting-through-openvpn-with-deprecated-ciphers-using-docker-488j</guid>
      <description>&lt;p&gt;Caution! This is a developer horror story 👻, Halloween 2022 comes with a nightmare for those who use a VPN. OpenSSL released the version &lt;code&gt;3.0.7&lt;/code&gt; as the latest stable, if you compare that we're talking about a jump from &lt;code&gt;1.1.1&lt;/code&gt; to &lt;code&gt;3.0.7&lt;/code&gt; we can think that any already deprecated protocol was removed to guarantee the security of our systems! And, it happened.&lt;br&gt;
Arch Linux, with its philosophy of using the latest stable release of everything, applied the OpenSSL v3 on Saturday morning November 5.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Treating option '--ncp-ciphers' as  '--data-ciphers' (renamed in OpenVPN 2.5).&lt;br&gt;
WARNING: Compression for receiving enabled. Compression has been used in the past to break encryption. Sent packets are not compressed unless "allow-compression yes" is also set.&lt;br&gt;
--cipher is not set. Previous OpenVPN version defaulted to BF-CBC as fallback when cipher negotiation failed in this case. If you need this fallback please add '--data-ciphers-fallback BF-CBC' to your configuration and/or add BF-CBC to --data-ciphers.&lt;br&gt;
Unsupported cipher in --data-ciphers: BF-CBC&lt;br&gt;
Options error: NCP cipher list contains unsupported ciphers or is too long.&lt;br&gt;
Use --help for more information.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Surprise! You can't use the &lt;code&gt;BF-CBC&lt;/code&gt; cipher on OpenVPN anymore, because it was removed from OpenSSL itself; OpenVPN plans to remove it on 2.7 but we're currently in 2.5.8 at the moment.&lt;/p&gt;

&lt;p&gt;Downgrading the &lt;code&gt;openssl&lt;/code&gt; package is a possible solution but not the best. Should I move my development environment to a virtual machine? 🙀 Change to an old Ubuntu version? Not for me. Here is when the hero 🦸 of this story appears to save us, and its name is Docker 🐳.&lt;/p&gt;

&lt;p&gt;💡 You can receive an alternative error message in Ubuntu or other Linux distributions:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OpenVPN 2.5.5 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Jul 14 2022&lt;br&gt;
library versions: OpenSSL 3.0.2 15 Mar 2022, LZO 2.10&lt;br&gt;
WARNING: --ns-cert-type is DEPRECATED.  Use --remote-cert-tls instead.&lt;br&gt;
OpenSSL: error:0A00018E:SSL routines::ca md too weak&lt;br&gt;
Cannot load inline certificate file&lt;br&gt;
Exiting due to fatal error&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;The containers can use the same network of the host, this avoids the container being network isolated, and the VPN tunnel is shared with our host system.&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating the image
&lt;/h3&gt;

&lt;p&gt;For this example I'll use a simple &lt;code&gt;ovpn&lt;/code&gt; file, but with a few tweaks I'm sure this will work for you too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:focal&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; openvpn &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; profile.ovpn /etc/openvpn/client/&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["openvpn", "/etc/openvpn/client/profile.ovpn"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We based our image on Ubuntu 20.04 LTS (Focal Fossa), just install the &lt;code&gt;openvpn&lt;/code&gt; package and copy your OpenVPN configuration file inside. Maybe you need a &lt;code&gt;.p12&lt;/code&gt;, &lt;code&gt;.key&lt;/code&gt;, or &lt;code&gt;.crt&lt;/code&gt; files just to copy them too.&lt;/p&gt;

&lt;p&gt;🧠 We can't use Ubuntu 22.04 LTS (Jammy Jellyfish) because it received the update to OpenSSL 3.0.2.&lt;/p&gt;

&lt;p&gt;Build the image with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; openvpn &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Starting the container
&lt;/h3&gt;

&lt;p&gt;Now, we can run the container!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vpn &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cap-add&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;NET_ADMIN &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;host &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--device&lt;/span&gt; /dev/net/tun &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-it&lt;/span&gt; openvpn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The container needs the Linux kernel capability of network administration to create a VPN tunnel with the &lt;code&gt;--cap-add=NET_ADMIN&lt;/code&gt; argument. Also &lt;code&gt;--device /dev/net/tun&lt;/code&gt; gives access to the tunnel device of the host.&lt;/p&gt;

&lt;p&gt;⚠️ Don't give kernel capabilities or access to devices if you don't trust the publisher.&lt;/p&gt;

&lt;p&gt;Once started, the container could ask for your credentials (username and password) to establish the VPN connection and it's done! 🎉&lt;/p&gt;

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

&lt;p&gt;Some changes can't be applied in enterprise environments without analyzing all possible scenarios, and the change of this cipher could take some time. That's where Docker could help us to continue using old software in a controlled way for specific tasks without compromising the security of our system.&lt;/p&gt;

&lt;p&gt;I hope you don't get scared with this story, but Super-Docker saves the day, one more time. Tell me in the comments if this helped you, or if you found another solution.&lt;/p&gt;

</description>
      <category>tutorials</category>
      <category>docker</category>
      <category>devops</category>
      <category>secops</category>
    </item>
    <item>
      <title>Using Git with two or more users</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Tue, 16 Aug 2022 12:53:00 +0000</pubDate>
      <link>https://community.ops.io/navarroaxel/using-git-with-two-or-more-users-85p</link>
      <guid>https://community.ops.io/navarroaxel/using-git-with-two-or-more-users-85p</guid>
      <description>&lt;p&gt;Sometimes we need to use more than one Git user in the same machine. This is simple to configure but we need to know a few things before using this feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating an additional SSH key
&lt;/h2&gt;

&lt;p&gt;By default the SSH key is generated in the &lt;code&gt;~/.ssh/&lt;/code&gt; directory, and it's called &lt;code&gt;id_rsa&lt;/code&gt; or &lt;code&gt;id_ed25519&lt;/code&gt;, depending on the signature type. To create a key with a specific filename we can use the following command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-f&lt;/span&gt; filename &lt;span class="nt"&gt;-t&lt;/span&gt; ed25519 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s1"&gt;'machine_name'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 If you're going to use this key for a specific organization, it's very useful to use the format &lt;code&gt;id_orgname&lt;/code&gt; for the filename.&lt;/p&gt;

&lt;p&gt;The comment specified by using the &lt;code&gt;-C&lt;/code&gt; argument should be something useful to know why this key was created 🤔. It could be the name of the machine that uses this SSH key or any notation that helps you. This comment is only visible by you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can we use the same key for all the users?
&lt;/h3&gt;

&lt;p&gt;It depends. The source code hostings, like GitHub, associate the SSH key to a specific user of the platform to know which user is pushing code to a Git repository, check the access, etc. If your company doesn’t use Github, con can also use the same key for a personal email in Github and your company’s email in GitLab.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloning a repository with a specific SSH key
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;git clone&lt;/code&gt; command uses the SSH protocol to connect to the Git hosting, and the SSH program in your machine uses the default key (usually located at &lt;code&gt;~/.ssh/id_ed25519&lt;/code&gt;) for authentication. You should use a custom SSH command to clone the repository using this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--config&lt;/span&gt; core.sshCommand&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ssh -i path/to/private_ssh_key"&lt;/span&gt; git@github.com:orgname/repo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧠 Remember that, if the key name ends with &lt;code&gt;.pub&lt;/code&gt;, e.g. &lt;code&gt;id_orgname.pub&lt;/code&gt;, then that is the public key. You should use the key file without this extension because that's your private key.&lt;/p&gt;

&lt;p&gt;If you want to use a different email address, configure it with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config user.name &lt;span class="s2"&gt;"John Doe"&lt;/span&gt;
git config user.email john@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 Use the &lt;code&gt;--global&lt;/code&gt; argument to set default settings for all the Git repositories in the machine. Otherwise the &lt;code&gt;git config&lt;/code&gt; command only sets configuration values for the repository in the current directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing the key to an already cloned repository
&lt;/h2&gt;

&lt;p&gt;If you've cloned a Git repository but you need to use a new SSH key to push code, you can use this command to set a new SSH command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config core.sshCommand &lt;span class="s2"&gt;"ssh -i path/to/private_ssh_key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Extra SSH tips
&lt;/h2&gt;

&lt;p&gt;Maybe your company has their Git repositories in a custom port 🙈 (&lt;code&gt;22&lt;/code&gt; is the default port for SSH), you need to clone these repositories with the following option:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--config&lt;/span&gt; core.sshCommand&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ssh -p 2222"&lt;/span&gt; git@git.example.com:path/to/repo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;You can use specific Git configurations for each repository, like commit author's information or specific command aliases.&lt;/p&gt;

&lt;p&gt;The variations for the SSH commands to connect to a repository using the SSH protocol can be changed anytime.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>productivity</category>
      <category>tutorials</category>
    </item>
    <item>
      <title>How to use databases in the Docker world</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Sun, 24 Jul 2022 19:01:37 +0000</pubDate>
      <link>https://community.ops.io/navarroaxel/how-to-use-databases-in-the-docker-world-4dj8</link>
      <guid>https://community.ops.io/navarroaxel/how-to-use-databases-in-the-docker-world-4dj8</guid>
      <description>&lt;p&gt;We can run DB engines locally on our machine without installing them by using Docker containers.&lt;/p&gt;

&lt;p&gt;💡 Remember that the container's data is removed with the container itself, so, we're going to store the DB data into a specific &lt;a href="https://docs.docker.com/storage/volumes/"&gt;Docker volume&lt;/a&gt; (&lt;code&gt;--volume&lt;/code&gt;) to keep it through time.&lt;/p&gt;

&lt;h2&gt;
  
  
  MongoDB
&lt;/h2&gt;

&lt;p&gt;We can start a MongoDB instance with this simple command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--detach&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; mongo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 27017:27017 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; mongo:/data/db &lt;span class="se"&gt;\&lt;/span&gt;
  mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This starts a Docker container that runs the &lt;code&gt;mongod&lt;/code&gt; process and listens on the default MongoDB port &lt;code&gt;27017&lt;/code&gt;. By default MongoDB uses the &lt;code&gt;test&lt;/code&gt; database and only accepts connections from &lt;code&gt;localhost&lt;/code&gt; without authentication.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Docker run command
&lt;/h3&gt;

&lt;p&gt;Let's explain the &lt;code&gt;docker run&lt;/code&gt; arguments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;--detach&lt;/code&gt; runs the container in background, like a service.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--name&lt;/code&gt; assigns a specific name to the created container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--publish&lt;/code&gt; forwards a specific port in &lt;code&gt;localhost&lt;/code&gt; to the given container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--volume&lt;/code&gt; creates or reuses a volume and attaches it to the given container at the specified path.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By default MongoDB uses the &lt;code&gt;/data/db&lt;/code&gt; directory to store the databases (remember to mount a Docker volume in that path).&lt;/p&gt;

&lt;h3&gt;
  
  
  A MongoDB root user
&lt;/h3&gt;

&lt;p&gt;You can specify a &lt;code&gt;root&lt;/code&gt; user to the DB adding the following environment variables to your the container&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--detach&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; mongo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;MONGO_INITDB_ROOT_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mongoadmin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;MONGO_INITDB_ROOT_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 27017:27017 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; mongo:/data/db &lt;span class="se"&gt;\&lt;/span&gt;
  mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more variables check the &lt;code&gt;mongo&lt;/code&gt; image page in &lt;a href="https://hub.docker.com/_/mongo"&gt;Docker hub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using the MongoDB shell
&lt;/h3&gt;

&lt;p&gt;You can connect to your DB via terminal by using the &lt;code&gt;mongosh&lt;/code&gt; command in the same container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mongo mongosh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;--username&lt;/code&gt; and &lt;code&gt;--password&lt;/code&gt; if those values are required.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to update the engine
&lt;/h3&gt;

&lt;p&gt;We can update a DB instance easily if we have previously stored the DB data in a Docker volume‼️ This is important, otherwise we'll lose data.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Pull the latest MongoDB image&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;First stop and remove the Docker container&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker stop mongo
docker &lt;span class="nb"&gt;rm &lt;/span&gt;mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Recreate the container with the same given arguments, remember to include the &lt;code&gt;--env&lt;/code&gt; variables (in this case, just &lt;code&gt;MONGO_INITDB_ROOT_USERNAME&lt;/code&gt; and &lt;code&gt;MONGO_INITDB_ROOT_PASSWORD&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--detach&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; mongo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 27017:27017 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; mongo:/data/db &lt;span class="se"&gt;\&lt;/span&gt;
  mongo
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's done! your database is now up to date. 🙌&lt;/p&gt;

&lt;h2&gt;
  
  
  PostgreSQL
&lt;/h2&gt;

&lt;p&gt;For Postgres remember to set the root user password.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--detach&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; pg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; pgdata:/var/lib/postgresql/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 5432:5432 &lt;span class="se"&gt;\&lt;/span&gt;
  postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default the username is &lt;code&gt;postgres&lt;/code&gt; but you can override that value using the &lt;code&gt;POSTGRES_USER&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;For more variables check the &lt;a href="https://hub.docker.com/_/postgres"&gt;Docker Hub&lt;/a&gt; page.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting via terminal
&lt;/h3&gt;

&lt;p&gt;You can connect via terminal using the same container with the correct username instead of installing the client on your machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pg psql &lt;span class="nt"&gt;--username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The password is not required to connections from the same host.&lt;/p&gt;

&lt;h2&gt;
  
  
  MySQL
&lt;/h2&gt;

&lt;p&gt;MySQL follows the Postgres rules with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--detach&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; mysql &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; mysql:/var/lib/mysql &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 3306:3306 &lt;span class="se"&gt;\&lt;/span&gt;
  mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More information in &lt;a href="https://hub.docker.com/_/mysql"&gt;Docker hub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting via terminal
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mysql mysql &lt;span class="nt"&gt;-p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client will prompt for the root password.&lt;/p&gt;

&lt;h2&gt;
  
  
  Microsoft SQL Server
&lt;/h2&gt;

&lt;p&gt;Microsoft needs that you declare that you have a license to the SQL Server that you want to use with &lt;code&gt;ACCEPT_EULA=Y&lt;/code&gt;. 🙄&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; mssql &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;ACCEPT_EULA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Y &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="s1"&gt;'SA_PASSWORD=yourStrong(!)Password'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; mssql:/var/opt/mssql &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 1433:1433 &lt;span class="se"&gt;\&lt;/span&gt;
  mcr.microsoft.com/mssql/server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, the SA password has stronger constraints than the other engines.&lt;/p&gt;

&lt;h3&gt;
  
  
  SQL Server editions
&lt;/h3&gt;

&lt;p&gt;By default the Developer edition is the SQL Server edition used by this Docker image. If you need a SQL Express edition you should use &lt;code&gt;MSSQL_PID=Express&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; mssql &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;ACCEPT_EULA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Y &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="nv"&gt;MSSQL_PID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Express &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--env&lt;/span&gt; &lt;span class="s1"&gt;'SA_PASSWORD=yourStrong(!)Password'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--volume&lt;/span&gt; mssql:/var/opt/mssql &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--publish&lt;/span&gt; 1433:1433 &lt;span class="se"&gt;\&lt;/span&gt;
  mcr.microsoft.com/mssql/server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More information about SQL server editions in &lt;a href="https://hub.docker.com/_/microsoft-mssql-server"&gt;Docker hub&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting via terminal
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;sqlcmd&lt;/code&gt; command is not in &lt;code&gt;PATH&lt;/code&gt;, so you should use the absolute path to execute it inside the container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mssql /opt/mssql-tools/bin/sqlcmd &lt;span class="nt"&gt;-U&lt;/span&gt; sa &lt;span class="nt"&gt;-P&lt;/span&gt; &lt;span class="s1"&gt;'yourStrong(!)Password'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Now, we know how to handle MongoDB and some SQL engines using Docker containers, using volumes to store the data.&lt;/p&gt;

&lt;p&gt;We can connect to the database terminal using the container itself, and we must use Docker volume to prevent data loss.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>database</category>
      <category>mongodb</category>
      <category>postgres</category>
    </item>
    <item>
      <title>ugrep: an interactive grep for the terminal</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Wed, 08 Jun 2022 13:43:39 +0000</pubDate>
      <link>https://community.ops.io/navarroaxel/ugrep-an-interactive-grep-for-the-terminal-2o95</link>
      <guid>https://community.ops.io/navarroaxel/ugrep-an-interactive-grep-for-the-terminal-2o95</guid>
      <description>&lt;p&gt;The &lt;code&gt;ugrep&lt;/code&gt; tool is a faster, user-friendly and compatible &lt;code&gt;grep&lt;/code&gt; replacement written in C++11. Let's check how to install &lt;code&gt;ugrep&lt;/code&gt; and how to use the interactive query mode, fuzzy search, and other options.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is &lt;code&gt;grep&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;If you are a terminal user, &lt;code&gt;grep&lt;/code&gt; is a basic tool that you must learn. The &lt;code&gt;grep&lt;/code&gt; command is a filter that prints lines from a file that contain a match for one or more patterns.&lt;/p&gt;

&lt;p&gt;E.g. &lt;code&gt;grep&lt;/code&gt; can help you to find specific output in a log file like the following:&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="nb"&gt;grep &lt;/span&gt;10.0.0.1 server.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this post we're going to focus in the &lt;code&gt;ugrep&lt;/code&gt; app, an extension of this tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;In Windows, you can just download the full-featured &lt;a href="https://github.com/Genivia/ugrep/releases"&gt;ugrep.exe&lt;/a&gt; from the &lt;a href="https://github.com/Genivia/ugrep"&gt;official repository&lt;/a&gt;, or use a tool like: &lt;code&gt;choco install ugrep&lt;/code&gt; or &lt;code&gt;scoop install ugrep&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In MacOS, you could use &lt;code&gt;brew install ugrep&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Or, find your destiny here: &lt;a href="https://github.com/Genivia/ugrep#install"&gt;https://github.com/Genivia/ugrep#install&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basics
&lt;/h2&gt;

&lt;p&gt;The basic usage is start the interactive query mode and look for a specific text in recursively in sub-directories using plain-text or regex.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ugrep &lt;span class="nt"&gt;-Q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://community.ops.io/images/6PYgNwe57_iqTni3zx3FEPTTknKuhN-eBHzLbOmLkg0/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvYjNx/c2Q5b2cwNW55cG41/MHZnaXYucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/6PYgNwe57_iqTni3zx3FEPTTknKuhN-eBHzLbOmLkg0/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvYjNx/c2Q5b2cwNW55cG41/MHZnaXYucG5n" alt="ugrep query mode in tldr pages repository" width="880" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💡 Tip: you can add the &lt;code&gt;--ignore-case&lt;/code&gt; argument to make case-insensitive matches.&lt;/p&gt;

&lt;p&gt;If you want to see only the files that contains the search term you can use the following arguments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ugrep &lt;span class="nt"&gt;-Q&lt;/span&gt; &lt;span class="nt"&gt;--ignore-case&lt;/span&gt; &lt;span class="nt"&gt;--files-with-matches&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://community.ops.io/images/SPOaD-XkbM_2ODd8zpIYxNyhC9DqLoWfoa3r4AuhueE/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvOXls/bGdkZzY0ZTJ3bnY0/dmVnd3YucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/SPOaD-XkbM_2ODd8zpIYxNyhC9DqLoWfoa3r4AuhueE/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvOXls/bGdkZzY0ZTJ3bnY0/dmVnd3YucG5n" alt="ugrep query mode listing filenames in tldr pages repository" width="433" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The fuzzy search
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Approximate_string_matching"&gt;fuzzy search&lt;/a&gt; is the technique of look for a text that match a pattern approximately, instead of exactly. We can look for a text within the specified &lt;a href="https://en.wikipedia.org/wiki/Levenshtein_distance"&gt;Levenshtein distance&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ugrep &lt;span class="nt"&gt;-Z3&lt;/span&gt; android
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 Tip: &lt;code&gt;-Z3&lt;/code&gt; matches up to three extra, missing or replaced characters; &lt;code&gt;-Z+~3&lt;/code&gt; allows up to three insertions (&lt;code&gt;+&lt;/code&gt;) or substitutions (&lt;code&gt;~&lt;/code&gt;), but no deletions (&lt;code&gt;-&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;You can start a query mode with fuzzy search like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ugrep &lt;span class="nt"&gt;-Q&lt;/span&gt; &lt;span class="nt"&gt;-Z3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Interactive TUI
&lt;/h2&gt;

&lt;p&gt;Once you started the query mode, you can toggle fuzzy search, list only filenames, or more in options panel pressing &lt;code&gt;F1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/Jbr7VKJQA01PryguMZp3wHmBPf1PqAMKrHhwO5lsPzM/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZWVz/bXB2OXJuZTdtcmdu/YTI1MDgucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/Jbr7VKJQA01PryguMZp3wHmBPf1PqAMKrHhwO5lsPzM/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvZWVz/bXB2OXJuZTdtcmdu/YTI1MDgucG5n" alt="ugrep help and options" width="880" height="773"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press the upper case Z to toggle on/off the fuzzy search mode, and &lt;code&gt;[&lt;/code&gt; to increase and &lt;code&gt;]&lt;/code&gt; to decrease the fuzziness.&lt;/p&gt;

&lt;p&gt;Also, you can scroll the result of a search using the arrow keys or &lt;code&gt;^S&lt;/code&gt;, then with &lt;code&gt;F2&lt;/code&gt; you can open the editor for the file of the current matched text (nano, vim, etc).&lt;/p&gt;

&lt;h2&gt;
  
  
  More ugrep
&lt;/h2&gt;

&lt;p&gt;You have a lot of possible customization options you could investigate in the &lt;a href="https://github.com/Genivia/ugrep#readme"&gt;README&lt;/a&gt; or use &lt;a href="https://github.com/Genivia/ugrep#vim"&gt;ugrep with Vim&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://github.com/genivia-inc"&gt;Dr. Robert van Engelen&lt;/a&gt; for this awesome tool! and if you 💛 it too, leave a ⭐ in &lt;a href="https://github.com/Genivia/ugrep"&gt;https://github.com/Genivia/ugrep&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>bash</category>
      <category>tooling</category>
      <category>devops</category>
    </item>
    <item>
      <title>Sign your code</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Wed, 25 May 2022 19:52:15 +0000</pubDate>
      <link>https://community.ops.io/navarroaxel/sign-your-code-5cdk</link>
      <guid>https://community.ops.io/navarroaxel/sign-your-code-5cdk</guid>
      <description>&lt;p&gt;Did you know you can sign your code in Git using a GPG key? A lot of programmers don't know they can sign their Git commits using a signature created by themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;You can use any name or email when you create a Git commit, but you can't sign a commit with a GPG key that doesn't belong to you.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is GPG?
&lt;/h3&gt;

&lt;p&gt;The GNU Privacy Guard is an implementation of the OpenPGP standard and allows you to encrypt and sign your data and communications.&lt;/p&gt;

&lt;p&gt;This is fully integrated with Git and you can automatically sign your code with it.&lt;/p&gt;

&lt;p&gt;You can create a GPG key following &lt;a href="https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key"&gt;this guide&lt;/a&gt; in GitHub. Remember that you should register it in &lt;a href="https://github.com/settings/keys"&gt;GitHub&lt;/a&gt;, or &lt;a href="https://gitlab.com/-/profile/gpg_keys"&gt;GitLab&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should I sign every commit?
&lt;/h2&gt;

&lt;p&gt;There is a discussion about this, because Linus Torvalds says that when you're signing a Git tag 🏷️, you're validating all the commits in the release, signed or not. If you &lt;strong&gt;automatically&lt;/strong&gt; sign every commit in the repository, the source of the signature loses its sense.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/iAyxX5HbVBK7KQEZIbK8z3xiGcbe--cFBJiQbckadcM/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvdjR2/aHVpdngxZ2ljbnRw/MWtoeXEucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/iAyxX5HbVBK7KQEZIbK8z3xiGcbe--cFBJiQbckadcM/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvdjR2/aHVpdngxZ2ljbnRw/MWtoeXEucG5n" alt="Signed tag by a Node.js' member on GitHub" width="599" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the other hand, some Linux distros, like Arch Linux, uses Arch User (Git) Repositories made by users to build non-official supported packages. I mean, if you're compiling and installing a package via another user's instructions maybe you like the idea of the authors signing their work. ⚠️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/XTgUnLo_Xn5LjqW4VFH3uyALAi5PB3iXjonmNzhtWoE/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvMzJz/b2oxbDJxMzZrNDd3/NDhoN2EucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/XTgUnLo_Xn5LjqW4VFH3uyALAi5PB3iXjonmNzhtWoE/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvMzJz/b2oxbDJxMzZrNDd3/NDhoN2EucG5n" alt="A signed commit to build the WebStorm EAP package in Arch Linux-based operating systems" width="880" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I personally like the idea of signing my open source contributions, because that's a proof of my work. Anyway, if you use GitHub to &lt;a href="https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-pull-request-commits"&gt;squash your commits&lt;/a&gt; before merging a pull request, GitHub replaces all your commits, signed or not, by a commit made by itself and signed with the GitHub GPG key. The same happens when you create code releases via the GitHub web interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/R8qZs4HGf5K60gPQlyEjInRWNsgj65UTf5qwyeexwE8/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvb2J4/dTh2NXFhMG83enl0/aHl0aDgucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/R8qZs4HGf5K60gPQlyEjInRWNsgj65UTf5qwyeexwE8/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL3VwbG9hZHMv/YXJ0aWNsZXMvb2J4/dTh2NXFhMG83enl0/aHl0aDgucG5n" alt="Signed commits in GitHub with a partially signed commit" width="880" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to sign a Git commit?
&lt;/h2&gt;

&lt;p&gt;First, get your key ID and copy it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gpg &lt;span class="nt"&gt;--list-secret-keys&lt;/span&gt; &lt;span class="nt"&gt;--keyid-format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;long
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, tell Git your GPG key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.signingkey 3AA5C34371567BD2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can sign using the &lt;code&gt;-S&lt;/code&gt; argument&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="nt"&gt;-am&lt;/span&gt; &amp;lt;your_commit_message&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and it's done! 🚀&lt;/p&gt;

&lt;p&gt;💡 To sign every commit automatically you can use the following configuration without needing the &lt;code&gt;-S&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; commit.gpgsign &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to sign a Git tag?
&lt;/h2&gt;

&lt;p&gt;When you create a tag locally you should add the &lt;code&gt;--sign&lt;/code&gt; argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git tag &lt;span class="nt"&gt;--sign&lt;/span&gt; &amp;lt;tag_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, you can turn on this using the following Git configuration setting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; tag.gpgsign &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 If you only want to apply this for the repository in the current directory, remove the &lt;code&gt;--global&lt;/code&gt; argument.&lt;/p&gt;

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

&lt;p&gt;If you work in the open source world it's a really good practice to sign your releases using a GPG key. If your work is consumed from a branch instead of a Git tag, perhaps you should sign every commit.&lt;/p&gt;

&lt;p&gt;And, if you work in a closed source repository, you can add a rule in your CI/CD tools to only allow releases with specific GPG keys.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>devops</category>
      <category>secops</category>
    </item>
    <item>
      <title>Publish an npm to GitHub packages</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Wed, 25 May 2022 19:47:36 +0000</pubDate>
      <link>https://community.ops.io/navarroaxel/publish-an-npm-to-github-packages-3o2a</link>
      <guid>https://community.ops.io/navarroaxel/publish-an-npm-to-github-packages-3o2a</guid>
      <description>&lt;p&gt;Sometimes in website and backend projects we found common components (React, utils, validations, etc) and, if we follow the DRY concept, we should find a way to create a private package and install it in every project that needs it.&lt;/p&gt;

&lt;p&gt;We're going to review how to build and publish JavaScript packages using GitHub and npm.&lt;/p&gt;

&lt;h2&gt;
  
  
  The GitHub solution
&lt;/h2&gt;

&lt;p&gt;GitHub provides the &lt;a href="https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-npm-registry"&gt;GitHub Package Registry&lt;/a&gt; to publish private npm packages. We can also use it for Docker images and libraries for other languages like Ruby, but we're going to focus on the npm solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  The publish configuration
&lt;/h3&gt;

&lt;p&gt;You should add the &lt;code&gt;publishConfig&lt;/code&gt; section in the &lt;code&gt;package.json&lt;/code&gt; file to publish to the GitHub registry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"publishConfig"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"registry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://npm.pkg.github.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  The workflow
&lt;/h3&gt;

&lt;p&gt;This workflow will publish a package to the GitHub Registry every time we create a release in the GitHub repository:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;created&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;publish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
      &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run build&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo @lexacode:https://npm.pkg.github.com/ &amp;gt; build/.npmrc&lt;/span&gt;
          &lt;span class="s"&gt;echo '//npm.pkg.github.com/:_authToken=${NPM_TOKEN}' &amp;gt;&amp;gt; build/.npmrc&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm publish&lt;/span&gt;
        &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./build&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;NPM_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;a href="https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#permissions"&gt;&lt;code&gt;permissions&lt;/code&gt;&lt;/a&gt; modifies the default permissions granted to the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This workflow creates a &lt;code&gt;.npmrc&lt;/code&gt; file inside the &lt;code&gt;build/&lt;/code&gt; directory before publishing the package to the registry.&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="nb"&gt;echo&lt;/span&gt; @lexacode:https://npm.pkg.github.com/ &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; build/.npmrc
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'//npm.pkg.github.com/:_authToken=${NPM_TOKEN}'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; build/.npmrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;🧠 Remember that your organization name, e.g. &lt;code&gt;lexacode&lt;/code&gt;, should be in &lt;code&gt;kebab-case&lt;/code&gt;, &lt;strong&gt;no uppercase allowed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Then, you should add the &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; as an environment variable for the &lt;code&gt;npm publish&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm publish&lt;/span&gt;
  &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./build&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;NPM_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Installing the GitHub package
&lt;/h2&gt;

&lt;p&gt;To install the GitHub package locally you should create a PAT (Personal Access Token) in the GitHub &lt;a href="https://github.com/settings/tokens/new"&gt;web&lt;/a&gt;. Make sure you selected the &lt;code&gt;read:packages&lt;/code&gt; permission. Then, add the token to your environment:&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NPM_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;YOUR_GITHUB_TOKEN&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Create the following &lt;code&gt;.npmrc&lt;/code&gt; file in the project:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@lexacode:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, you can run the &lt;code&gt;npm install&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @lexacode/package-example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  In GitHub Actions
&lt;/h3&gt;

&lt;p&gt;To use your package in GitHub actions you should use a code like the following:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
      &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v2&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;NPM_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;👉 You need the explicit &lt;code&gt;packages: read&lt;/code&gt; permission.&lt;/p&gt;
&lt;h3&gt;
  
  
  Packages cross organizations
&lt;/h3&gt;

&lt;p&gt;If you want to use a package from another organization using the GitHub Package Registry, you should set your PAT as a &lt;a href="https://docs.github.com/en/actions/security-guides/encrypted-secrets"&gt;secret&lt;/a&gt; in the repository. Edit the YAML file using the new secret instead:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;NPM_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.NPM_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;💡 The custom &lt;code&gt;permissions&lt;/code&gt; section is not required for this scenario.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You can publish private packages and use it everywhere you want via the GitHub Package Registry.&lt;/p&gt;

&lt;p&gt;I left you a full repository with a TypeScript package, already published using the CI action. 🙌&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/lexacode"&gt;
        lexacode
      &lt;/a&gt; / &lt;a href="https://github.com/lexacode/package-example"&gt;
        package-example
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Example npm package&lt;/h1&gt;
&lt;p&gt;This repository is a template for create TypeScript packages compatible with ES modules and CommonJS.&lt;/p&gt;
&lt;/div&gt;

  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/lexacode/package-example"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>npm</category>
      <category>github</category>
      <category>devops</category>
    </item>
    <item>
      <title>Docker commit: converting a container into an image</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Wed, 25 May 2022 19:46:46 +0000</pubDate>
      <link>https://community.ops.io/navarroaxel/docker-commit-converting-a-container-into-an-image-1e4g</link>
      <guid>https://community.ops.io/navarroaxel/docker-commit-converting-a-container-into-an-image-1e4g</guid>
      <description>&lt;p&gt;We can convert a specific container into a base Docker image, but that's not the recommended way to create images because these could include useless cached or session data. &lt;br&gt;
The easiest way to control the data stored in the Docker layers is using the &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;🧠 Remember that a Docker image has a read-only filesystem based on layers which works as a &lt;em&gt;template&lt;/em&gt; to start containers in a preinstalled environment, and a Docker container uses this &lt;em&gt;"template"&lt;/em&gt; to run isolated applications into a virtualized run-time environment with a normal filesystem.&lt;/p&gt;
&lt;h2&gt;
  
  
  Is this necessary to create images with non-root users?
&lt;/h2&gt;

&lt;p&gt;No, you can run commands with other users using the &lt;code&gt;Dockerfile&lt;/code&gt; as well. You can see an example &lt;a href="https://dev.to/cloudx/testing-our-package-build-in-the-docker-world-34p0"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Adding a port forwarding rule to a container
&lt;/h2&gt;

&lt;p&gt;When you are testing a random software within a container you might need to publish a port on the host OS, or into another computer. In this case you can't add a forwarding port rule to an already existing container, so here is when docker commit comes to save us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ docker commit my_container my_image
$ docker run -p 8080:80 -it my_image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can access to the port &lt;code&gt;80&lt;/code&gt; in the container using &lt;code&gt;http://localhost:8080&lt;/code&gt; in the host OS.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker commit&lt;/code&gt; can also be used to attach new volumes to the container.&lt;/p&gt;

&lt;h2&gt;
  
  
  The size is important
&lt;/h2&gt;

&lt;p&gt;Remember that it's a good practice to keep your Docker images as minimalist as possible. Also the reproducible way to create and ship images is the &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;💡 You can &lt;a href="https://dev.to/cloudx/analyzing-the-docker-layers-with-dive-5e7o"&gt;inspect the layers of a Docker image&lt;/a&gt; using Dive, to optimize the size of your images.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>linux</category>
      <category>devops</category>
    </item>
    <item>
      <title>Analyzing the docker layers with dive</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Wed, 25 May 2022 19:41:58 +0000</pubDate>
      <link>https://community.ops.io/navarroaxel/analyzing-the-docker-layers-with-dive-18j0</link>
      <guid>https://community.ops.io/navarroaxel/analyzing-the-docker-layers-with-dive-18j0</guid>
      <description>&lt;p&gt;Dive is a tool to explore a docker image, layer contents and discover ways to shrink the size of your Docker image written in Go.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do the layers work?
&lt;/h2&gt;

&lt;p&gt;We should start with the concept of layer in Docker - we can say layers are like git commits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;they have a parent layer,&lt;/li&gt;
&lt;li&gt;are readonly,&lt;/li&gt;
&lt;li&gt;and receive an ID calculated via SHA256 hash.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why is «readonly» important?
&lt;/h3&gt;

&lt;p&gt;Because if you edit or remove a file, that file still exists inside your docker image.&lt;/p&gt;

&lt;p&gt;So, we must clean temporal or useless files in the same &lt;code&gt;RUN&lt;/code&gt; sentence to keep our images small.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; apt-transport-https ca-certificates &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same rule applies when we build an app in a &lt;code&gt;RUN&lt;/code&gt; sentence. We must clean the intermediate files and only keep the useful ones.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a docker tag?
&lt;/h3&gt;

&lt;p&gt;The Docker tags, like &lt;code&gt;focal&lt;/code&gt; in &lt;code&gt;ubuntu:focal&lt;/code&gt;, are just pointers to a layer. Unlike git tags, in Docker, we accept that a tag can point to a different layer when the image is updated.&lt;/p&gt;

&lt;p&gt;💡 TIP: the image is not updated because the layers are readonly, it's a new image. 🤯&lt;/p&gt;

&lt;h2&gt;
  
  
  What Dive does?
&lt;/h2&gt;

&lt;p&gt;With Dive we can inspect the layers of a Docker image and see the difference from the parent layer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dive node:alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://community.ops.io/images/VH0dHyvPtoAfzuLz7_Pn_etQb-9lKs7BEfU6FU5JJxM/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL2kvNDQ5OHlt/ZXVyNXhyaHRjd3Fw/M2kucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/VH0dHyvPtoAfzuLz7_Pn_etQb-9lKs7BEfU6FU5JJxM/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL2kvNDQ5OHlt/ZXVyNXhyaHRjd3Fw/M2kucG5n" alt="Analyzing the node alpine image" width="880" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the left panel, we can see the layers of the given image, the command which generates the selected layer in purple 💜, the ID and the digest of that selected layer.&lt;/p&gt;

&lt;p&gt;In the right panel, we can see the &lt;code&gt;diff&lt;/code&gt; of the layer with a color reference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🟢 green: the new files.&lt;/li&gt;
&lt;li&gt;🟡 yellow: the edited files.&lt;/li&gt;
&lt;li&gt;🟥 red: the deleted files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Did you see that &lt;code&gt;/tmp&lt;/code&gt; folder in the layer? 🧐 now, I wonder if the &lt;code&gt;/tmp&lt;/code&gt; folder with a &lt;a href="https://www.npmjs.com/package/v8-compile-cache"&gt;v8-compile-cache&lt;/a&gt; could be not committed to the layer, reducing the size of the &lt;code&gt;node:alpine&lt;/code&gt; image by &lt;code&gt;2.3MiB&lt;/code&gt;. 🤔&lt;/p&gt;

&lt;p&gt;There is a &lt;a href="https://github.com/nodejs/docker-node/pull/1283"&gt;PR&lt;/a&gt; to remove the &lt;code&gt;v8-compile-cache&lt;/code&gt; folder, but the cache «is used to speed up a little Yarn loading time from from 153ms to 113ms». Worth it?&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the efficiency score?
&lt;/h2&gt;

&lt;p&gt;Dive tries to help us by indicating the efficiency of our images. The edited or removed files reduce the efficiency score because the original files exist in the image but are not useful for the container.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.ops.io/images/LL_ROTFf5YKdkOR9dKg9Pzgjey33xlgJi7PeaoQiSQ0/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL2kvbXVpc2l1/ZHFpaTdpOXZncGpt/Z3gucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/LL_ROTFf5YKdkOR9dKg9Pzgjey33xlgJi7PeaoQiSQ0/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL2kvbXVpc2l1/ZHFpaTdpOXZncGpt/Z3gucG5n" alt="The node alpine efficiency" width="598" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Count&lt;/code&gt; column indicates how many times the same file is committed into the image.&lt;/p&gt;

&lt;h3&gt;
  
  
  The efficiency as linter
&lt;/h3&gt;

&lt;p&gt;You can run this in your CI pipeline to ensure you're keeping wasted space to a minimum:&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="nv"&gt;CI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true &lt;/span&gt;dive node:alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://community.ops.io/images/MHZqJZ_qhfMcJ227I43XCXpuYsGsZiUAJw_DsX63NvQ/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL2kvNjM2OTFt/OHZuazd3bzdubGJz/dWgucG5n" class="article-body-image-wrapper"&gt;&lt;img src="https://community.ops.io/images/MHZqJZ_qhfMcJ227I43XCXpuYsGsZiUAJw_DsX63NvQ/w:880/mb:500000/ar:1/aHR0cHM6Ly9kZXYt/dG8tdXBsb2Fkcy5z/My5hbWF6b25hd3Mu/Y29tL2kvNjM2OTFt/OHZuazd3bzdubGJz/dWgucG5n" alt="Dive fails with lower efficiency" width="880" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How is the efficiency score calculated?
&lt;/h3&gt;

&lt;p&gt;The score only takes note of the edited or removed files that consume space in the image.&lt;/p&gt;

&lt;p&gt;Maybe we can apply more validation rules that Dive doesn't have today.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check for content in the &lt;code&gt;/tmp&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;Check for apt, yum or pacman cache files.&lt;/li&gt;
&lt;li&gt;Check for a filename pattern, e.g: do not forget &lt;code&gt;*.tar.gz&lt;/code&gt; files inside the image.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  More Dive
&lt;/h2&gt;

&lt;p&gt;Thanks to &lt;a href="https://github.com/wagoodman"&gt;Alex Goodman&lt;/a&gt; for this awesome tool! and if you 💛 it, leave a ⭐ in &lt;a href="https://github.com/wagoodman/dive"&gt;https://github.com/wagoodman/dive&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>cicd</category>
      <category>devops</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Rust and the hidden cargo clippy</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Wed, 25 May 2022 19:40:40 +0000</pubDate>
      <link>https://community.ops.io/navarroaxel/rust-and-the-hidden-cargo-clippy-1cim</link>
      <guid>https://community.ops.io/navarroaxel/rust-and-the-hidden-cargo-clippy-1cim</guid>
      <description>&lt;p&gt;Clippy is a «collection of lints to catch common mistakes and improve your Rust code». 💪&lt;br&gt;
But for some reason Clippy looks like a hidden treasure inside the &lt;code&gt;cargo&lt;/code&gt; command. I found this by accident checking the code of the &lt;a href="https://dev.to/cloudx/a-new-npm-is-coming-5heh"&gt;Orogene&lt;/a&gt; tool.&lt;/p&gt;
&lt;h2&gt;
  
  
  How can I get clippy?
&lt;/h2&gt;

&lt;p&gt;Now, Clippy is inside &lt;code&gt;cargo&lt;/code&gt; but is not listed when you run the &lt;code&gt;cargo --help&lt;/code&gt; command. The same happens with &lt;code&gt;cargo fmt&lt;/code&gt; to format your code, maybe there are more easter eggs inside &lt;code&gt;cargo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Also, there is a &lt;a href="https://github.com/rust-lang/rust-clippy#as-a-cargo-subcommand-cargo-clippy"&gt;guide&lt;/a&gt; about how to add &lt;code&gt;clippy&lt;/code&gt; to cargo if this is not present in your CLI.&lt;/p&gt;
&lt;h2&gt;
  
  
  And what does clippy do?
&lt;/h2&gt;

&lt;p&gt;As any linters, clippy has a list of warnings like &lt;a href="https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy"&gt;manual_memcpy&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;What it does
Checks for for-loops that manually copy items between slices that could be optimized by having a memcpy.

Why is this bad
It is not as fast as a memcpy.

Example
for i in 0..src.len() {
    dst[i + 64] = src[i];
}

Could be written as:
dst[64..(src.len() + 64)].clone_from_slice(&amp;amp;src[..]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a long list of lint errors &lt;a href="https://rust-lang.github.io/rust-clippy/master/index.html"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to run this command?
&lt;/h2&gt;

&lt;p&gt;Clippy builds your code and then runs the linter, so Clippy receives the same arguments as the &lt;code&gt;cargo check&lt;/code&gt; subcommand.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo clippy &lt;span class="c"&gt;# Run in the default package(s).&lt;/span&gt;
cargo clippy &lt;span class="nt"&gt;--release&lt;/span&gt; &lt;span class="c"&gt;# Run in release mode&lt;/span&gt;
cargo clippy &lt;span class="nt"&gt;--workspace&lt;/span&gt; &lt;span class="c"&gt;# Checks the packages in the monorepo&lt;/span&gt;
cargo build &lt;span class="nt"&gt;--package&lt;/span&gt; myPkgName &lt;span class="c"&gt;# An specific page&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, after the &lt;code&gt;--&lt;/code&gt; you can add the arguments for clippy itself:&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="nv"&gt;RUSTFLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-Dwarnings"&lt;/span&gt; cargo clippy &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; warnings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this argument the build job will fail when encountering warnings. The &lt;code&gt;RUSTFLAGS="-Dwarnings"&lt;/code&gt; environment variable is required to get this effect but the README doesn't mention it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The custom configuration
&lt;/h2&gt;

&lt;p&gt;You can configure the lint rules in a file called &lt;code&gt;clippy.toml&lt;/code&gt;, e.g. you can turn the &lt;code&gt;pedantic&lt;/code&gt; rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#![deny(clippy::pedantic)]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These rules contain some very aggressive lints prone to false positives. 🤭&lt;/p&gt;

&lt;p&gt;You can check more examples &lt;a href="https://github.com/rust-lang/rust-clippy#configuration"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  The CI config
&lt;/h1&gt;

&lt;p&gt;The best config for CI is checking the code format, then linting in the code behavior, and finally running the tests that you wrote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# set env RUSTFLAGS="-Dwarnings"&lt;/span&gt;
&lt;span class="s"&gt;cargo fmt --workspace -- --check&lt;/span&gt;
&lt;span class="s"&gt;cargo clippy --workspace -- -D warnings&lt;/span&gt;
&lt;span class="s"&gt;cargo test --workspace&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  That's all! And... Isn't it awesome?
&lt;/h2&gt;

&lt;p&gt;To spend a minute and use a lint tool, in my opinion, is one of those things that marks a difference and makes your code professional, clean and performant.&lt;/p&gt;

&lt;p&gt;If you don't use them, I encourage you to start now! There are a lot of basic guides out there.&lt;/p&gt;

&lt;p&gt;If you love to use them, now you know a hidden new trick (feels good, right?)&lt;/p&gt;

&lt;h2&gt;
  
  
  Tell me what you think!
&lt;/h2&gt;

&lt;p&gt;Have you ever found or implement useful tools in Rust? 🦀 Have you improved your code with Clippy?&lt;/p&gt;

&lt;p&gt;Tell me about it in the comments!&lt;/p&gt;

</description>
      <category>rust</category>
      <category>devops</category>
      <category>github</category>
      <category>cicd</category>
    </item>
  </channel>
</rss>
