<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Douglas Domingos :: CompSci@UFCG</title><link>https://dougdomingos.netlify.app/</link><description>Recent content on Douglas Domingos :: CompSci@UFCG</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sat, 04 Jan 2025 00:32:27 -0300</lastBuildDate><atom:link href="https://dougdomingos.netlify.app/index.xml" rel="self" type="application/rss+xml"/><item><title>My First Thoughts on Fedora Linux</title><link>https://dougdomingos.netlify.app/posts/my-first-thoughts-on-fedora-linux/</link><pubDate>Sat, 04 Jan 2025 00:32:27 -0300</pubDate><guid>https://dougdomingos.netlify.app/posts/my-first-thoughts-on-fedora-linux/</guid><description>Introduction Since my first contact using Linux back in 2020, I&amp;rsquo;ve tried out many distributions, such as Ubuntu, Linux Mint, Kubuntu, and most recently Pop!_OS. But, as you may already known, these are all Debian-based distributions, so aside from graphical interfaces, they function in the exact same way.
Having expanded my knowledge in Linux throughout the last year, I now feel confortable enough to tweak with more cutting-edge systems. My first idea was to try Manjaro KDE, as Arch-based distributions seemed quite interesting.</description><content>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>Since my first contact using Linux back in 2020, I&amp;rsquo;ve tried out many
distributions, such as &lt;strong>Ubuntu&lt;/strong>, &lt;strong>Linux Mint&lt;/strong>, &lt;strong>Kubuntu&lt;/strong>, and most
recently &lt;strong>Pop!_OS&lt;/strong>. But, as you may already known, these are all
&lt;strong>Debian-based distributions&lt;/strong>, so aside from graphical interfaces, &lt;strong>they
function in the exact same way&lt;/strong>.&lt;/p>
&lt;p>Having expanded my knowledge in Linux throughout the last year, I now feel
confortable enough to tweak with more &lt;strong>cutting-edge systems&lt;/strong>. My first idea
was to try &lt;strong>Manjaro KDE&lt;/strong>, as Arch-based distributions seemed quite
interesting. Yet, after playing around with &lt;strong>Fedora&lt;/strong> through LiveUSB, I&amp;rsquo;ve
decided to stick around with it for now.&lt;/p>
&lt;p>The opinions shared in this post are based on the following use cases:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Software Development&lt;/strong>, mainly in &lt;strong>Java&lt;/strong>, &lt;strong>JavaScript&lt;/strong> and &lt;strong>Python&lt;/strong>
environments&lt;/li>
&lt;li>&lt;strong>Casual tasks and applications&lt;/strong> (e.g. music and video streaming, web
browsing, etc.)&lt;/li>
&lt;li>&lt;strong>Gaming&lt;/strong> (i.e. lightweight games via &lt;strong>SteamOS&lt;/strong> and &lt;strong>Proton&lt;/strong>)&lt;/li>
&lt;/ul>
&lt;p>I&amp;rsquo;ve also considered the &lt;strong>opinions of other users&lt;/strong> posted on the Reddit
community &lt;a href="https://www.reddit.com/r/Fedora/">r/Fedora&lt;/a>, which &lt;strong>I recommend you
to follow&lt;/strong> for troubleshooting and customizing configurations.&lt;/p>
&lt;h2 id="the-first-contact">The first contact&lt;/h2>
&lt;p>Something noticeable even &lt;em>before&lt;/em> installing Fedora is that it&amp;rsquo;s very
&lt;strong>lightweight&lt;/strong> - it&amp;rsquo;s shipped only with &lt;strong>Firefox&lt;/strong>, &lt;strong>LibreOffice Suite&lt;/strong>, and
basic applications/utilities. This also applies for &lt;strong>GNOME Shell&lt;/strong>, &lt;strong>the
graphical interface of Fedora Workstation&lt;/strong>, which offers a &lt;strong>pure, vanilla
experience&lt;/strong>. Sure, finding and testing GNOME extensions for &lt;strong>basic
funcionalities&lt;/strong> (e.g. adding app shortcuts to the desktop) is quite
&lt;strong>annoying&lt;/strong>, but it&amp;rsquo;s a classic &amp;ldquo;install and forget&amp;rdquo; type of job.&lt;/p>
&lt;p>The &lt;em>real&lt;/em> deal-maker for me was &lt;strong>the package repositories&lt;/strong>: many of the
softwares I use are avaliable in DNF with &lt;strong>more recent versions&lt;/strong> than those in
APT-based distributions. Also, Fedora comes with &lt;strong>Flatpak&lt;/strong> pre-installed, only
requiring you to &lt;strong>add the FlatHub repositories&lt;/strong> before installing software.&lt;/p>
&lt;p>However, not everything is perfect: Fedora&amp;rsquo;s preference for &lt;strong>open-source
software&lt;/strong> means that you may need to &lt;strong>manually install proprietary drivers&lt;/strong>,
such as those for &lt;strong>Nvidia GPUs&lt;/strong>, and &lt;strong>media codecs&lt;/strong> like H.264. Fortunately,
most of these &lt;strong>can be found in RPMFusion&lt;/strong> - Fedora&amp;rsquo;s third-party repository.
So far, I only had to install a few &lt;strong>video codecs for VLC&lt;/strong>, without any
struggles with hardware compatibility. Therefore, your experience with Fedora
may vary, depending on &lt;strong>your hardware specifications&lt;/strong> and &lt;strong>which programs you
use&lt;/strong>.&lt;/p>
&lt;h2 id="software-compatibility">Software compatibility&lt;/h2>
&lt;p>One of my biggest &lt;strong>concerns&lt;/strong> when moving away from &lt;strong>Pop!_OS&lt;/strong> was that some
applications I use &lt;strong>wouldn&amp;rsquo;t be compatible&lt;/strong>, without any satisfactory
alternatives. Thankfully, &lt;strong>this was not the case&lt;/strong>, as Fedora&amp;rsquo;s repositories
&lt;strong>provided pretty much all the packages&lt;/strong> I needed. But again, for &lt;strong>third-party
packages&lt;/strong> and &lt;strong>proprietary software&lt;/strong>, you may have to &lt;strong>reach out for
RPMFusion&lt;/strong>, which is very easy to setup and use.&lt;/p>
&lt;p>On the rare ocasions where the softwares you need is &lt;strong>not within Fedora&amp;rsquo;s
repositories nor RPMFusion&lt;/strong>, you can also use &lt;strong>Flatpak&lt;/strong> - a package manager
that allows you to &lt;strong>install and execute containerized applications&lt;/strong>, keeping
their dependencies separate from the system. &lt;strong>I personally prefer flatpaks&lt;/strong>
over native packages, though I&amp;rsquo;ve seen &lt;strong>many users recommend the opposite&lt;/strong>.&lt;/p>
&lt;p>The &lt;em>only&lt;/em> cases I believe you may have to &lt;strong>opt for alternatives&lt;/strong> are for
packages and applications &lt;strong>distributed only through Snap or PPAs&lt;/strong>. For Snap,
however, it&amp;rsquo;s probable that you&amp;rsquo;ll find your software within Flatpak or RPM
repositories, which was my case for &lt;strong>Hugo SSG&lt;/strong> - the framework used in this
website, distributed in Fedora through DNF.&lt;/p>
&lt;h2 id="support-and-resources">Support and resources&lt;/h2>
&lt;p>It&amp;rsquo;s important to highlight that &lt;strong>Fedora has far less online resources&lt;/strong> when
compared to Ubuntu/Debian distributions, which means that you may have
&lt;strong>difficulties finding solutions&lt;/strong> for technical problems, specially if you have
&lt;strong>little experience with Linux&lt;/strong>.&lt;/p>
&lt;p>Despite that, I&amp;rsquo;ve found Fedora to be &lt;strong>very similar in usability&lt;/strong> with Ubuntu
distributions so far. &lt;strong>The most noticeable difference&lt;/strong> between the platforms
are &lt;strong>the package repositories&lt;/strong>, as Fedora (and RedHat Linux distributions)
enphatizes the &lt;strong>adoption of Open-Source software&lt;/strong>, and thus requires add some
&lt;strong>overhead configuration to allow proprietary packages&lt;/strong> - which are easily set
with only two commands in your terminal.&lt;/p>
&lt;p>Although Fedora has proved to be &lt;strong>stable and easy to use&lt;/strong> up until now, &lt;strong>I&amp;rsquo;d
still recommend Debian-based distributions&lt;/strong> for those who have no experience in
Linux, or simply have no time to spare on debugging configurations.&lt;/p>
&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>With my current experience, Fedora stands out on the following:&lt;/p>
&lt;ul>
&lt;li>Offers &lt;strong>a true vanilla GNOME experience&lt;/strong>, leaving all customizations for the
users&lt;/li>
&lt;li>Allows users to choose whether to &lt;strong>install proprietary software&lt;/strong> (through
RPMFusion) or not&lt;/li>
&lt;li>Packages within RPM repositories are offered in &lt;strong>more recent versions&lt;/strong> than
in APT&lt;/li>
&lt;/ul>
&lt;p>However, I see some possible drawbacks of this migration:&lt;/p>
&lt;ul>
&lt;li>Being a cutting-edge distribution, Fedora may be &lt;strong>less stable than Ubuntu&lt;/strong>&lt;/li>
&lt;li>Specific applications &lt;strong>may not be available&lt;/strong> for Fedora&lt;/li>
&lt;li>Potential &lt;strong>reduced compatibility&lt;/strong> with older hardware (based on some reports
from other users)&lt;/li>
&lt;/ul>
&lt;p>Some of this aspects may change as my experience with Fedora evolves, so expect
updates in the future.&lt;/p></content></item><item><title>Creating a Multiboot USB Drive with Ventoy</title><link>https://dougdomingos.netlify.app/posts/multiboot-usb-with-ventoy/</link><pubDate>Wed, 20 Nov 2024 18:49:01 -0300</pubDate><guid>https://dougdomingos.netlify.app/posts/multiboot-usb-with-ventoy/</guid><description>Introduction During a deploy of our custom Linux Mint images in one of the labs at UFCG, I&amp;rsquo;ve learned about Ventoy – an interesting tool for creating bootable USB drives. Of all the tools I&amp;rsquo;ve used for this task (like Rufus and Etcher), Ventoy catched my interest for three reasons:
It does not require flashing the ISO into the USB drive It allows you to add multiple ISO files to the USB drive, without tweaking with disk partitions It allows you to choose which image to boot from at startup Ventoy is also very easy to install and use, as it provides packages with graphical interface for Linux, so you don&amp;rsquo;t have to worry with cryptic shell commands.</description><content>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>During a deploy of our custom Linux Mint images in one of the labs at UFCG, I&amp;rsquo;ve
learned about &lt;a href="https://www.ventoy.net/en/index.html">Ventoy&lt;/a> – an interesting
tool for &lt;strong>creating bootable USB drives&lt;/strong>. Of all the tools I&amp;rsquo;ve used for this
task (like Rufus and Etcher), Ventoy catched my interest for three reasons:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>It does not require flashing the ISO&lt;/strong> into the USB drive&lt;/li>
&lt;li>It allows you to &lt;strong>add multiple ISO files&lt;/strong> to the USB drive, without
tweaking with disk partitions&lt;/li>
&lt;li>It allows you to &lt;strong>choose which image to boot from at startup&lt;/strong>&lt;/li>
&lt;/ol>
&lt;p>Ventoy is also very easy to install and use, as &lt;strong>it provides packages with
graphical interface&lt;/strong> for Linux, so you don&amp;rsquo;t have to worry with cryptic shell
commands. But that&amp;rsquo;s boring, so we&amp;rsquo;re going to get our hands dirty anyways
&amp;#x1f971;&lt;/p>
&lt;h2 id="how-ventoy-works">How Ventoy works?&lt;/h2>
&lt;p>While installing Ventoy on an USB drive, it will create two main partitions:&lt;/p>
&lt;ul>
&lt;li>The EFI partition (formatted as &lt;code>FAT&lt;/code>), where Ventoy&amp;rsquo;s bootloader and
configurations are stored, and only 32MB in size&lt;/li>
&lt;li>The main partition (usually formatted as &lt;code>exFAT&lt;/code> or &lt;code>FAT32&lt;/code>), where you can
place your ISO files&lt;/li>
&lt;/ul>
&lt;p>When booting from the USB drive, &lt;strong>Ventoy&amp;rsquo;s bootloader will look for ISO files&lt;/strong>
inside the main partition and &lt;strong>list them at the boot menu&lt;/strong>. Please note that
the disk layout may vary, depending on whether you choose the
&lt;a href="https://www.ventoy.net/en/doc_disk_layout.html">MBR&lt;/a> or
&lt;a href="https://www.ventoy.net/en/doc_disk_layout_gpt.html">GPT&lt;/a> partitioning scheme.&lt;/p>
&lt;h2 id="using-ventoy-on-linux">Using Ventoy on Linux&lt;/h2>
&lt;p>As mentioned before, we&amp;rsquo;ll be installing Ventoy through CLI – like &lt;em>real&lt;/em> Linux
users would. So, you&amp;rsquo;ll need to download the binary packages (e.g.
&lt;code>ventoy-1.0.XX-linux.tar.gz&lt;/code> file), which are avaliable at
&lt;a href="https://sourceforge.net/projects/ventoy/files/">SourceForge&lt;/a>. I recommend
unpacking the tarball at &lt;code>/opt&lt;/code> for convenience.&lt;/p>
&lt;p>Inside the package, you&amp;rsquo;ll see a bunch of executable files. Since we won&amp;rsquo;t use a
graphical interface, our focus is in the &lt;code>Ventoy2Disk.sh&lt;/code> script.&lt;/p>
&lt;blockquote>
&lt;p>If you wish to install Ventoy using a graphical interface, &lt;strong>execute one of
the files labeled with the &lt;code>VentoyGUI&lt;/code> prefix&lt;/strong>. Please note that you have to
choose &lt;strong>the executable with the same architecture as your machine&lt;/strong> (e.g.
&lt;code>VentoyGUI.x86_64&lt;/code> for 64-bit machines).&lt;/p>
&lt;p>You may also &lt;strong>use an Web UI&lt;/strong> by executing the &lt;code>VentoyWeb.sh&lt;/code> and opening
your browser at &lt;code>http://localhost:24680&lt;/code>&lt;/p>
&lt;/blockquote>
&lt;p>If you plan to use Ventoy frequently, it might be interesting to &lt;strong>add the
executables to your &lt;code>$PATH&lt;/code>&lt;/strong>. For that, I have an article on how to install
packages manually on Linux, which you can find
&lt;a href="https://dougdomingos.github.io/v2/posts/installing-packages-in-opt">here&lt;/a>.&lt;/p>
&lt;h2 id="installing-ventoy-on-a-usb-drive">Installing Ventoy on a USB drive&lt;/h2>
&lt;p>Now that we have the package set up on our machine, let&amp;rsquo;s take a look on how to
use &lt;code>Ventoy2Disk.sh&lt;/code>. After running the script without any arguments, we get the
following output:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>[...]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Usage: Ventoy2Disk.sh CMD [ OPTION ] /dev/sdX
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> CMD:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> -i install Ventoy to sdX (fails if disk already installed with Ventoy)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> -I force install Ventoy to sdX (no matter if installed or not)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> -u update Ventoy in sdX
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> -l list Ventoy information in sdX
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> OPTION: (optional)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> -r SIZE_MB preserve some space at the bottom of the disk (only for install)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> -s/-S enable/disable secure boot support (default is enabled)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> -g use GPT partition style, default is MBR (only for install)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> -L Label of the 1st exfat partition (default is Ventoy)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> -n try non-destructive installation (only for install)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Although the text is pretty straightforward, we need to find &lt;strong>which device is
our USB drive&lt;/strong> – specified by &lt;code>/dev/sdX&lt;/code>. For that, you can use the &lt;code>lsblk&lt;/code>
command to list all the block devices in the system. Here&amp;rsquo;s a sample output for
this command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>user@host:~# lsblk
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[...]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sda 8:0 0 238,5G 0 disk
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>├─sda3 8:3 0 237,5G 0 part /
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>└─sda4 8:4 0 1G 0 part /boot/efi
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sdb 8:16 1 7,5G 0 disk
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>├─sdb1 8:17 1 7,5G 0 part /media/dougsilva/Ventoy
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>└─sdb2 8:18 1 32M 0 part
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>[...]
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>USB devices are usually mounted at the &lt;code>/media&lt;/code> directory&lt;/strong>. In my case, the
USB drive is represented by &lt;code>sdb&lt;/code> device, so I must pass &lt;code>/dev/sdb&lt;/code> as the
installation device to the &lt;code>Ventoy2Disk.sh&lt;/code> script. Below are some examples on
how to configure your installation:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span># Install Ventoy on /dev/sdX. This will fail if Ventoy is already installed.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo ./Ventoy2Disk.sh -i /dev/sdX
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># Force install on /dev/sdX, ignoring any existent Ventoy installation
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo ./Ventoy2Disk.sh -I /dev/sdX
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># Install on /dev/sdX while keeping a 500MB free space at the end of the device
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo ./Ventoy2Disk.sh -i -r 500 /dev/sdX
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span># Install on /dev/sdX with GPT partition style and labelling the main partition as &amp;#34;Multiboot USB&amp;#34;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo ./Ventoy2Disk.sh -i -g -L &amp;#34;Multiboot USB&amp;#34; /dev/sdX
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Please note that installing Ventoy &lt;strong>WILL ERASE ALL DATA FROM THE DEVICE&lt;/strong>, so
you should backup any important files from the USB drive before installation.
Also, while installing through command-line, &lt;strong>be careful to not mistake your
USB drive for another device&lt;/strong>.&lt;/p>
&lt;blockquote>
&lt;p>You can also use the &lt;code>fdisk -l&lt;/code> command to list the devices of your machine
with a more verbose output. This allows you to search for a device with &lt;code>grep&lt;/code>
(i.e. &lt;code>fdisk -l | grep &amp;quot;YOUR_DEVICE&amp;quot;&lt;/code>).&lt;/p>
&lt;/blockquote>
&lt;h2 id="booting-up-the-usb-drive">Booting up the USB drive&lt;/h2>
&lt;p>After the installation is complete, you can simply drag-and-drop your ISO files
into the main partition, reboot your machine and boot from the USB drive. If you
had no unpleasant surprises during installation, you should see the following
screen:&lt;/p>
&lt;p>&lt;img alt="Ventoy Menu screen" src="https://dougdomingos.netlify.app/images/ventoy-iso-menu.png" title="Ventoy Menu screen">&lt;/p>
&lt;p>From here, simply choose which ISO you wish to boot, and select the
&lt;code>Boot in normal mode&lt;/code> option. For more information about boot modes, read
&lt;a href="https://www.ventoy.net/en/doc_secondary_boot_menu.html">the documentation&lt;/a>.&lt;/p>
&lt;blockquote>
&lt;p>Some machines have &lt;strong>a hot key for entering the Boot Menu&lt;/strong> at startup, so you
don&amp;rsquo;t need to &lt;strong>manually change the boot order&lt;/strong> in your BIOS. Please check
out &lt;a href="https://www.disk-image.com/faq-bootmenu.htm">this list&lt;/a> for reference.&lt;/p>
&lt;/blockquote>
&lt;h2 id="considerations-about-secure-boot">Considerations about Secure Boot&lt;/h2>
&lt;p>Since Ventoy 1.0.76, &lt;strong>Secure Boot support is enabled by default&lt;/strong>. This means
that Ventoy should work &lt;strong>whether Secure Boot is enabled in your machine or
not&lt;/strong>.&lt;/p>
&lt;p>If Secure Boot is indeed enabled in your machine, you should see the screen
below when booting Ventoy for the first time. Please follow these instructions
in &lt;a href="https://www.ventoy.net/en/doc_secure.html">Ventoy&amp;rsquo;s documentation&lt;/a> on how to
allow Ventoy on Secure Boot.&lt;/p>
&lt;p>&lt;img alt="Ventoy&amp;rsquo;s Secure Boot prompt screen" src="https://dougdomingos.netlify.app/images/sb-first-boot.png" title="Ventoy's Secure Boot prompt screen">&lt;/p>
&lt;p>However, if you see &lt;strong>another error screen&lt;/strong> while booting Ventoy, &lt;strong>your system
may not support Ventoy on Secure Boot&lt;/strong>. As such, you&amp;rsquo;ll need to disable the
option in your Ventoy installation and your BIOS.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Until now, Ventoy has proved to be a handy tool for testing Linux distributions
and keeping backup ISOs for system recovery. It&amp;rsquo;s easy to install and use, it&amp;rsquo;s
lightweight and also quite fast when compared to similar tools.&lt;/p>
&lt;p>While I have not tried installing a system through Ventoy, I plan to try it out
soon with Fedora, and if everything goes as expected, I may use it to setup a
dual-boot installation with Windows in the (not so) far future.&lt;/p></content></item><item><title>Automating tasks with Github Actions</title><link>https://dougdomingos.netlify.app/posts/automating-tasks-with-github-actions/</link><pubDate>Fri, 01 Nov 2024 17:35:48 -0300</pubDate><guid>https://dougdomingos.netlify.app/posts/automating-tasks-with-github-actions/</guid><description>Introduction In software development, it’s common to have some tasks that need to be executed regularly, at a specific time or in a certain order. Usually, teams would write dedicated scripts for these tasks, which adds complexity and diverts focus from the main product.
That&amp;rsquo;s where CI/CD platforms comes in, streamlining development workflows by automating repetitive tasks so developers can focus on the actual software. Here, we’ll be exploring one of these platforms: GitHub Actions.</description><content>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>In software development, it’s common to have some tasks that need to be executed
regularly, at a specific time or in a certain order. Usually, teams would write
dedicated scripts for these tasks, which &lt;strong>adds complexity and diverts focus&lt;/strong>
from the main product.&lt;/p>
&lt;p>That&amp;rsquo;s where &lt;strong>CI/CD platforms&lt;/strong> comes in, streamlining development workflows by
automating repetitive tasks so developers can focus on the actual software.
Here, we’ll be exploring one of these platforms: &lt;strong>GitHub Actions&lt;/strong>.&lt;/p>
&lt;h2 id="what-are-github-actions">What are GitHub Actions?&lt;/h2>
&lt;p>&lt;strong>GitHub Actions&lt;/strong> is a &lt;strong>CI/CD platform integrated directly into GitHub&lt;/strong>,
which allows you to easily automate tasks within repositories. &lt;strong>Each repository
can have multiple workflows&lt;/strong>, represented by YAML files located in the
&lt;code>.github/workflows/&lt;/code> directory. As you&amp;rsquo;ll see, this makes workflows &lt;strong>easy to
write and understand&lt;/strong>.&lt;/p>
&lt;p>You can define &lt;strong>workflows triggered by various events&lt;/strong>, such as pushes and
pull requests, and use them for a wide range of tasks — testing, deploying to
production, enforcing code style, and much more. Workflows can even be
&lt;strong>scheduled&lt;/strong>, &lt;strong>combined with other workflows&lt;/strong>, and used to &lt;strong>update your
repositories&lt;/strong>. Plus, &lt;strong>you can leverage actions created by other developers&lt;/strong>!&lt;/p>
&lt;h2 id="setting-up-an-workflow">Setting up an workflow&lt;/h2>
&lt;p>New workflows can be added to your repository in two ways:&lt;/p>
&lt;ul>
&lt;li>Through &lt;a href="https://github.com/">GitHub&lt;/a>, where you can choose from many template
workflows and adjust them as needed&lt;/li>
&lt;li>&lt;strong>By manually creating the workflow file&lt;/strong> inside &lt;code>.github/workflows&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Regardless of your choice, describing workflows is writing YAML files. And, to
fully understand the structure of a workflow, let&amp;rsquo;s create one that simply
prints &amp;ldquo;Hello Actions!&amp;rdquo; at each push made to the repository.&lt;/p>
&lt;h2 id="workflow-structure">Workflow structure&lt;/h2>
&lt;p>Implementing our &amp;ldquo;Hello Actions&amp;rdquo; workflow is as simple as this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">Simple Action&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">on&lt;/span>: &lt;span style="color:#ae81ff">push&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">jobs&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">print-msg&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">runs-on&lt;/span>: &lt;span style="color:#ae81ff">ubuntu-latest&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">steps&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">Print message&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">run&lt;/span>: &lt;span style="color:#ae81ff">echo &amp;#34;Hello Actions!&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, let&amp;rsquo;s understand what each part of this code does:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;code>name&lt;/code>, as it suggests, defines the name of the workflow. While it is
optional, &lt;strong>I strongly recommend&lt;/strong> to always specify the workflow name — not
only for readability, but also to &lt;strong>allow workflow composition&lt;/strong>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>on&lt;/code> defines the &lt;strong>events that will trigger the workflow&amp;rsquo;s execution&lt;/strong>. You
can specify &lt;strong>multiple triggers&lt;/strong> for the same workflow, and even &lt;strong>restrict
the event scope&lt;/strong> (e.g. pull requests to a specific branch, or changes made to
a specific file).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>jobs&lt;/code> describe &lt;strong>the actions performed by the workflow&lt;/strong>. An workflow can
have multiple jobs (that are executed on parallel by default), where each job
is &lt;strong>identified by an unique name&lt;/strong>.&lt;/p>
&lt;ul>
&lt;li>Each job must define &lt;strong>the environment it runs on&lt;/strong> (which is specified by
the &lt;code>runs-on&lt;/code> key)&lt;/li>
&lt;li>A job describes &lt;strong>the tasks it executes as a sequence of &lt;code>steps&lt;/code>&lt;/strong>. Each
step can execute commands, setup tasks and even other actions&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h2 id="example-1-automating-tests-for-pull-requests-made-to-the-main-branch">Example 1: Automating tests for pull requests made to the &lt;code>main&lt;/code> branch&lt;/h2>
&lt;p>&lt;strong>Automating tests plays a crucial role at code maintainabilty&lt;/strong>, as it ensures
that &lt;strong>changes made to the codebase do not break what previously worked&lt;/strong>. Such
task can be easily achieved with GitHub Actions by setting up &lt;strong>an workflow that
is executed for every pull request&lt;/strong> made to the repository.&lt;/p>
&lt;p>Consider a project written in Java with Maven as build tool. We want to
&lt;strong>automatically run all test classes&lt;/strong> for every pull request made to the &lt;code>main&lt;/code>
branch. This can be achieved with the following workflow:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">Run tests with Maven&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># The workflow only runs for pull requests made to the &amp;#34;main&amp;#34; branch&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">on&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">pull_requests&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">branches&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;main&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">jobs&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">run-tests&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">runs-on&lt;/span>: &lt;span style="color:#ae81ff">ubuntu-latest&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">steps&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># Checks out (e.g. clones) the repository into the Actions runner&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#f92672">uses&lt;/span>: &lt;span style="color:#ae81ff">actions/checkout@v4&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># Install the required Java version&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">Set up Java 17&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">uses&lt;/span>: &lt;span style="color:#ae81ff">actions/setup-java@v4&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">with&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">java-version&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;17&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">distribution&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;temurin&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">cache&lt;/span>: &lt;span style="color:#ae81ff">maven&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># Run tests with Maven&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">Test with Maven&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">run&lt;/span>: &lt;span style="color:#ae81ff">mvn test&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="example-2-scheduled-updates-of-a-repository">Example 2: Scheduled updates of a repository&lt;/h2>
&lt;p>In my &lt;a href="https://github.com/dougdomingos/portfolio-v1">old website&lt;/a>, I&amp;rsquo;ve set up
Next.js to regularly fetch my repository list from
&lt;a href="https://api.github.com">GitHub API&lt;/a>, so that my project list would always be
updated. After migrating to &lt;a href="https://gohugo.io/">Hugo&lt;/a>, I kept wondering on how
to implement this functionality with as little overhead as possible.&lt;/p>
&lt;p>It turns out that &lt;strong>GitHub Actions was perfect for my scenario&lt;/strong>. Simply write a
workflow that &lt;strong>fetches data from GitHub API&lt;/strong> and &lt;strong>commits the updated list
into the repository&lt;/strong>. After some &lt;em>googling&lt;/em> (and a little help from ChatGPT), I
was able to come up with an working solution.&lt;/p>
&lt;p>While &lt;strong>I chose to use Python for the fetch script&lt;/strong>, you could write a similar
solution in plain old Shell Script (though the equivalent code would likely be
&lt;em>much&lt;/em> more verbose) or in any language you prefer.&lt;/p>
&lt;p>First, here&amp;rsquo;s my Python script for fetching and parsing the data, at
&lt;code>.github/scripts/fetch_data.py&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">import&lt;/span> requests
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">import&lt;/span> yaml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">fetch_github_repos&lt;/span>(username: str):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;&amp;#34;&amp;#34;Fetch the repositories of a user from GitHub API
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74"> Args:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74"> username (str): The username of the account to be fetched
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74"> Returns:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74"> The contents of the response as a JSON object
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> FETCH_URL &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">f&lt;/span>&lt;span style="color:#e6db74">&amp;#34;https://api.github.com/users/&lt;/span>&lt;span style="color:#e6db74">{&lt;/span>username&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">/repos&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> response &lt;span style="color:#f92672">=&lt;/span> requests&lt;span style="color:#f92672">.&lt;/span>get(FETCH_URL)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> response&lt;span style="color:#f92672">.&lt;/span>status_code &lt;span style="color:#f92672">!=&lt;/span> &lt;span style="color:#ae81ff">200&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> print(&lt;span style="color:#e6db74">f&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Failed while fetching data: HTTP &lt;/span>&lt;span style="color:#e6db74">{&lt;/span>response&lt;span style="color:#f92672">.&lt;/span>status_code&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> []
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> response&lt;span style="color:#f92672">.&lt;/span>json()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">def&lt;/span> &lt;span style="color:#a6e22e">convert_to_yaml&lt;/span>(repo_list: list):
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;&amp;#34;&amp;#34;Given the contents of the request, create the equivalent YAML file.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74"> Args:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74"> repos_data (list): The JSON structure of repository
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># Order repositories by most recent updated&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> repo_list &lt;span style="color:#f92672">=&lt;/span> sorted(repo_list, key&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#66d9ef">lambda&lt;/span> repo: repo[&lt;span style="color:#e6db74">&amp;#34;updated_at&amp;#34;&lt;/span>], reverse&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#66d9ef">True&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> projects &lt;span style="color:#f92672">=&lt;/span> []
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> repo &lt;span style="color:#f92672">in&lt;/span> repo_list:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># You can add more properties here&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> project &lt;span style="color:#f92672">=&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;link&amp;#34;&lt;/span>: repo[&lt;span style="color:#e6db74">&amp;#34;html_url&amp;#34;&lt;/span>],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;info&amp;#34;&lt;/span>: {&lt;span style="color:#e6db74">&amp;#34;title&amp;#34;&lt;/span>: repo[&lt;span style="color:#e6db74">&amp;#34;name&amp;#34;&lt;/span>], &lt;span style="color:#e6db74">&amp;#34;description&amp;#34;&lt;/span>: repo[&lt;span style="color:#e6db74">&amp;#34;description&amp;#34;&lt;/span>]},
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;tags&amp;#34;&lt;/span>: repo[&lt;span style="color:#e6db74">&amp;#34;topics&amp;#34;&lt;/span>],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> projects&lt;span style="color:#f92672">.&lt;/span>append(project)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> data &lt;span style="color:#f92672">=&lt;/span> {&lt;span style="color:#e6db74">&amp;#34;projects&amp;#34;&lt;/span>: projects}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># Saves the project list as a YAML file&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">with&lt;/span> open(&lt;span style="color:#e6db74">&amp;#34;./data/projects.yaml&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;w&amp;#34;&lt;/span>) &lt;span style="color:#66d9ef">as&lt;/span> outfile:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> yaml&lt;span style="color:#f92672">.&lt;/span>dump(data, outfile, default_flow_style&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#66d9ef">False&lt;/span>, sort_keys&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#66d9ef">False&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> __name__ &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#e6db74">&amp;#34;__main__&amp;#34;&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> convert_to_yaml(fetch_github_repos(&lt;span style="color:#e6db74">&amp;#34;your_username&amp;#34;&lt;/span>))
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As for the workflow file, at &lt;code>.github/workflows/fetch_projects.yaml&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">Update project list&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Actions allow you to define environment variables too!&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">env&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">SCRIPT_PATH&lt;/span>: &lt;span style="color:#ae81ff">.github/scripts/fetch_projects.py&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Gives the workflow write permissions over the repository&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">permissions&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">contents&lt;/span>: &lt;span style="color:#ae81ff">write&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Workflow is scheduled to run every Sunday at 00h00&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">on&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">schedule&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#f92672">cron&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;0 0 * * 0&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">workflow_dispatch&lt;/span>: &lt;span style="color:#75715e"># this allows us to manually run the workflow in GitHub&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">jobs&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">update-list&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">runs-on&lt;/span>: &lt;span style="color:#ae81ff">ubuntu-latest&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">steps&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">Checkout&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">uses&lt;/span>: &lt;span style="color:#ae81ff">actions/checkout@v4&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">Set up Python&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">uses&lt;/span>: &lt;span style="color:#ae81ff">actions/setup-python@v5&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">with&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">python-version&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;3.10&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">Install dependencies&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">run&lt;/span>: &lt;span style="color:#ae81ff">pip install pyyaml requests&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">Run script&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">run&lt;/span>: &lt;span style="color:#ae81ff">python ${{ env.SCRIPT_PATH }}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#f92672">name&lt;/span>: &lt;span style="color:#ae81ff">Commit changes to repository&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">run&lt;/span>: |&lt;span style="color:#e6db74">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74"> git config --global user.name &amp;#34;&amp;lt;your-username-here&amp;gt;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74"> git config --global user.email &amp;#34;&amp;lt;your-email-here&amp;gt;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74"> git add ./data/projects.yaml
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74"> git commit -m &amp;#34;chore(actions): update project list&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74"> git push&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;blockquote>
&lt;p>&lt;strong>Cron syntax&lt;/strong>: Cron schedules are specified by strings composed of five
fields in a &lt;code>M H DM MN DW&lt;/code> format, as described below:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>M H DM MN DW
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| | | | |
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| | | | └── Day of the week (0-7, where 0 and 7 = Sunday)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| | | └───── Month (1-12)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| | └──────── Day of the month (1-31)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>| └─────────── Hour (0-23)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>└────────────── Minute (0-59)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>So, &lt;code>0 0 * * 0&lt;/code> means &amp;ldquo;at 00h00 of any day of any month on Sunday&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>Also, to ensure that the updated list is avaliable in the website after the
script is executed, I&amp;rsquo;ve added a &amp;ldquo;hook&amp;rdquo; to &lt;strong>trigger the &lt;code>build&lt;/code> workflow every
time the &lt;code>Update project list&lt;/code> workflow is executed&lt;/strong> — thus the importance of
&lt;strong>always naming your workflows&lt;/strong>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">on&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># Specifies that the &amp;#34;build&amp;#34; workflow should execute after the&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># &amp;#34;Update project list&amp;#34; workflow is completed&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">workflow_run&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">workflows&lt;/span>: [&lt;span style="color:#e6db74">&amp;#34;Update project list&amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">types&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">completed&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>GitHub Actions, as well as other CI/CD platforms, are &lt;strong>a powerful tool to
master&lt;/strong>. While I do believe this post may help you with the basics, &lt;strong>I
recommend taking a deeper look into the
&lt;a href="https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions">GitHub Actions documentation&lt;/a>&lt;/strong>.&lt;/p>
&lt;p>Also, feel free to use any of the workflows you&amp;rsquo;ve seen here in your projects.
Try adding some new features, or combining them with your own.&lt;/p>
&lt;p>That&amp;rsquo;s it for now. See ya! &amp;#x1f44b;&lt;/p></content></item><item><title>Installing software in Linux, the "hard" way</title><link>https://dougdomingos.netlify.app/posts/installing-packages-in-opt/</link><pubDate>Sun, 15 Sep 2024 13:34:18 -0300</pubDate><guid>https://dougdomingos.netlify.app/posts/installing-packages-in-opt/</guid><description>Introduction Picture this: you&amp;rsquo;re setting up the packages and tools to develop your brand new application. Everything is going smooth, until you found out that one of the softwares you need is only avaliable as package of executable binaries, without any installers and nowhere to be found in your package managers. Seems like quite the overhead, right?
Well, not to worry! Because today we&amp;rsquo;re out to get our hands dirty and learn how to install ANY PACKAGE in a Linux system ‒ and you might just learn a few new tricks about Linux along the way.</description><content>&lt;h2 id="introduction">Introduction&lt;/h2>
&lt;p>Picture this: you&amp;rsquo;re setting up the packages and tools to develop your brand new
application. Everything is going smooth, until you found out that &lt;strong>one of the
softwares you need is only avaliable as package of executable binaries&lt;/strong>,
without any installers and nowhere to be found in your package managers. Seems
like quite the overhead, right?&lt;/p>
&lt;p>Well, not to worry! Because today we&amp;rsquo;re out to get our hands dirty and learn how
to install &lt;strong>ANY PACKAGE&lt;/strong> in a Linux system ‒ and you might just learn a few
new tricks about Linux along the way.&lt;/p>
&lt;h2 id="i-housing-the-package">I. Housing the package&lt;/h2>
&lt;p>The first step to solve our problem is to define &lt;strong>where the package will be
stored in our system&lt;/strong>. In practice, you may use any directory you&amp;rsquo;d like, but
to keep things organized, let&amp;rsquo;s use the &lt;code>/opt&lt;/code> directory.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>The &lt;code>/opt&lt;/code> directory&lt;/strong> is reserved for installing software and packages that
are not part of the system&amp;rsquo;s core components, i.e, &lt;strong>not necessary for your
system to function&lt;/strong> properly.&lt;/p>
&lt;p>When installing your package, &lt;strong>you should create a new directory&lt;/strong>
(preferably with the package name, like &lt;code>/opt/&amp;lt;package_name&amp;gt;&lt;/code>) to &lt;strong>avoid
mixing up files&lt;/strong> of different packages.&lt;/p>
&lt;/blockquote>
&lt;p>You may have noticed that inside your package, there&amp;rsquo;s a &lt;code>/bin&lt;/code> directory that
contains some executable files. At this point, we can run these files, but we&amp;rsquo;ll
have to specify the full path of the file every time.&lt;/p>
&lt;h2 id="ii-make-your-package-avaliable-with-path">II. Make your package avaliable with &lt;code>$PATH&lt;/code>&lt;/h2>
&lt;p>To avoid having to pass the full path of the executables each time we run the
software, we&amp;rsquo;ll make use of the &lt;code>$PATH&lt;/code> environmental variable to &lt;strong>specify the
path of our installation&lt;/strong>.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>The &lt;code>$PATH&lt;/code> variable&lt;/strong> is a special shell variable that defines a &lt;strong>list of
directories where executable files are located&lt;/strong>. Each directory is
&lt;strong>delimited by a colon&lt;/strong> (&lt;code>:&lt;/code>)&lt;/p>
&lt;p>You may specify new directories in &lt;code>$PATH&lt;/code> by &lt;strong>exporting the variable with
the new value&lt;/strong> (&lt;code>export PATH=/new/dir/path:$PATH&lt;/code>).&lt;/p>
&lt;/blockquote>
&lt;p>Now, there are two ways of adding a executable to &lt;code>$PATH&lt;/code>:&lt;/p>
&lt;h3 id="adding-a-_symlink_-to-the-executables">Adding a &lt;em>Symlink&lt;/em> to the executables&lt;/h3>
&lt;p>&lt;em>Symlinks&lt;/em> are (roughly) &lt;strong>files that &amp;ldquo;point&amp;rdquo; to other file paths or
directories&lt;/strong>. This allows us to access files accross the file system (and even
other file systems), &lt;strong>without specifying the full path&lt;/strong>.&lt;/p>
&lt;p>You can create new &lt;em>symlinks&lt;/em> using the command &lt;code>ln -s TARGET LINK_NAME&lt;/code>, where:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;code>TARGET&lt;/code> is the &lt;strong>path of the file which the &lt;em>symlink&lt;/em> will point to&lt;/strong>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>LINK_NAME&lt;/code> is the &lt;strong>path where the &lt;em>symlink&lt;/em> will be created&lt;/strong>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>To make it avaliable in &lt;code>$PATH&lt;/code>, we may &lt;strong>create a &lt;em>symlink&lt;/em> inside a directory
that is already in &lt;code>$PATH&lt;/code>&lt;/strong>. In this case, let&amp;rsquo;s use the &lt;code>/usr/local/bin&lt;/code>
directory to store our &lt;em>symlink&lt;/em>.&lt;/p>
&lt;p>To create our &lt;em>symlink&lt;/em>, simply run the following comman:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Replace the placeholders with the proper names of your directories/files&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ln -s /opt/&amp;lt;package_name&amp;gt;/bin/&amp;lt;filename&amp;gt; /usr/local/bin/&amp;lt;filename&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once created, the executable will be avaliable in your shell. Try it out by
&lt;strong>typing the name of the &lt;em>symlink&lt;/em> in your terminal&lt;/strong>.&lt;/p>
&lt;h4 id="trade-offs-of-using-_symlinks_">Trade-offs of using &lt;em>symlinks&lt;/em>&lt;/h4>
&lt;p>This approach is &lt;strong>pretty straightforward and easy to setup&lt;/strong>. We don&amp;rsquo;t need to
edit the value of &lt;code>$PATH&lt;/code>, thus &lt;strong>avoiding possible configuration errors&lt;/strong>.
Also, by placing the &lt;em>symlink&lt;/em> inside &lt;code>/usr/local/bin&lt;/code>, we make it &lt;strong>avaliable
for all users of the system&lt;/strong>.&lt;/p>
&lt;p>Unfortunately, there are also drawbacks. If you need to add multiple
executables, you&amp;rsquo;ll need do &lt;strong>create one &lt;em>symlink&lt;/em> per executable&lt;/strong>. You may
&lt;strong>create a &lt;em>symlink&lt;/em> to the &lt;code>/bin&lt;/code> directory&lt;/strong> of the package, but you&amp;rsquo;ll still
need to &lt;strong>specify the name of the executable&lt;/strong>.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Links in Linux&lt;/strong>&lt;br>
Links are a powerful tool in Linux, allowing you to &lt;strong>create shortcuts between
files and directories&lt;/strong>, &lt;strong>organizing your files easily&lt;/strong>, and much more. I
recommend reading this
&lt;a href="https://www.redhat.com/sysadmin/linking-linux-explained">RedHat post about linking in Linux&lt;/a>.&lt;/p>
&lt;/blockquote>
&lt;h3 id="adding-the-package-directory-to-path">Adding the package directory to &lt;code>$PATH&lt;/code>&lt;/h3>
&lt;p>If you want a more &lt;strong>robust solution&lt;/strong>, you may effectivelly edit the &lt;code>$PATH&lt;/code>
variable to &lt;strong>include your package directory&lt;/strong>. For that, we&amp;rsquo;ll need to &lt;strong>create
a configuration file&lt;/strong> to export our updated &lt;code>$PATH&lt;/code>.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>The &lt;code>/etc/&lt;/code> directory&lt;/strong> contains configuration files of system applications,
e.g. service management, network, SSH, and many more. Be careful when making
changes to files here.&lt;/p>
&lt;/blockquote>
&lt;p>Similar to &lt;code>/opt&lt;/code>, Linux also has a directory for &lt;strong>storing system-wide
configuration files: the &lt;code>/etc&lt;/code> directory&lt;/strong>. Actually, there&amp;rsquo;s the &lt;code>/etc/opt&lt;/code>
directory, which is intended to store configuration files for &lt;strong>applications
installed in &lt;code>/opt&lt;/code>&lt;/strong>.&lt;/p>
&lt;p>To create the configuration file, head to &lt;code>/etc/opt&lt;/code> and create a new file,
named as &lt;code>&amp;lt;package_name&amp;gt;rc&lt;/code>. Inside it, paste the following code and replace the
placeholders with the correspondent paths:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Replace &amp;#34;PACKAGE_HOME&amp;#34; with the name of your package&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>export PACKAGE_HOME&lt;span style="color:#f92672">=&lt;/span>/opt/&amp;lt;package_dir&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>export PATH&lt;span style="color:#f92672">=&lt;/span>$PACKAGE_HOME/bin:$PATH
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To load the settings into your shell session, run
&lt;code>source /etc/opt/&amp;lt;config_file&amp;gt;&lt;/code> and check your &lt;code>$PATH&lt;/code> variable with
&lt;code>echo $PATH&lt;/code>. If the directory of your package is there, your configuration file
is working.&lt;/p>
&lt;p>Now, you might have noticed that &lt;strong>the configurations aren&amp;rsquo;t loaded
automatically&lt;/strong> when you open a new terminal. To address this, simply add this
configuration to one of the following files:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># This loads &amp;#34;PACKAGE&amp;#34; into $PATH&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">[&lt;/span> -f /etc/opt/&amp;lt;config_file&amp;gt; &lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> source /etc/opt/&amp;lt;config_file&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>
&lt;p>For &lt;strong>only one user&lt;/strong>, append the load configuration at the end of &lt;code>~/.bashrc&lt;/code>&lt;/p>
&lt;/li>
&lt;li>
&lt;p>For &lt;strong>all users&lt;/strong>, create a shell script (with the &lt;code>.sh&lt;/code> suffix) in
&lt;code>/etc/profile.d/&lt;/code> with the load configuration&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h4 id="trade-offs-of-using-path">Trade-offs of using &lt;code>$PATH&lt;/code>&lt;/h4>
&lt;p>One advantage of this approach is that &lt;strong>every executable inside the &lt;code>/bin&lt;/code>
directory will be avaliable&lt;/strong> in your shell. Also, you may choose to &lt;strong>make it
avaliable for only one user or for all users&lt;/strong>, by simply placing the load
configuration in a different file.&lt;/p>
&lt;p>However, &lt;strong>tweaking with the &lt;code>$PATH&lt;/code> variable may break your shell
configuration&lt;/strong> if you&amp;rsquo;re not careful enough. The same goes for editing files
inside &lt;code>/etc/profile.d&lt;/code>; it&amp;rsquo;s best to create a new file rather than edit an
existing one.&lt;/p>
&lt;h2 id="iii-updating-the-desktop-menu-bonus">III. Updating the desktop menu (BONUS)&lt;/h2>
&lt;p>If you’re installing an application with a graphical interface (not CLI-based),
you’ll likely want to add it to your desktop menu for easier access. In this
case, we&amp;rsquo;ll need to take an extra step.&lt;/p>
&lt;p>The entries in your desktop menu are located in the &lt;code>/usr/share/applications&lt;/code>
directory. Each application has a &lt;code>.desktop&lt;/code> entry file, containing settings for
how it is displayed in the menu.&lt;/p>
&lt;p>To add a desktop entry file for your application:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Create a new &lt;code>.desktop&lt;/code> entry file with the name of your application&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Open the file and add the following configuration, replacing the placeholders
with the correct settings:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">[&lt;/span>Desktop Entry&lt;span style="color:#f92672">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Version&lt;span style="color:#f92672">=&lt;/span>1.0
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Type&lt;span style="color:#f92672">=&lt;/span>Application
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Name&lt;span style="color:#f92672">=&lt;/span>&amp;lt;APP_NAME&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Icon&lt;span style="color:#f92672">=&lt;/span>/opt/&amp;lt;PACKAGE_NAME&amp;gt;/&amp;lt;ICON_FILE&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Exec&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;/opt/&amp;lt;PACKAGE_NAME&amp;gt;/&amp;lt;EXEC_FILE&amp;gt;&amp;#34;&lt;/span> %f
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Comment&lt;span style="color:#f92672">=&lt;/span>&amp;lt;APP_DESCRIPTION&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Categories&lt;span style="color:#f92672">=&lt;/span>&amp;lt;KEYWORD1;KEYWORD2;&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;/li>
&lt;li>
&lt;p>Save the file and run &lt;code>sudo update-desktop-database&lt;/code>&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>Now, the application is avaliable in your app menu, without the need to start it
through the terminal.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;ul>
&lt;li>
&lt;p>Install your package in the &lt;code>/opt&lt;/code> directory&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Use &lt;em>symlinks&lt;/em> or edit your &lt;code>$PATH&lt;/code> variable to include the package
executables for an user or all the system&lt;/p>
&lt;/li>
&lt;li>
&lt;p>If the package is not CLI-based, add a &lt;code>.desktop&lt;/code> entry file at
&lt;code>/usr/share/applications&lt;/code> to add the package to your desktop menu&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Although application support for Linux platforms is pretty decent today, you&amp;rsquo;ll
eventually come across a situation like the one we&amp;rsquo;ve discussed here. Also, you
can automate this process by creating shell scripts to setup the files for you.&lt;/p>
&lt;p>Well, that&amp;rsquo;s it. Until next time! &amp;#x1f44b;&lt;/p></content></item><item><title>About me</title><link>https://dougdomingos.netlify.app/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dougdomingos.netlify.app/about/</guid><description>Name Douglas Domingos Location Campina Grande, PB - Brazil Education B.Sc Candidate in Computer Science Skills Frontend, Backend, DevOps, Cloud Interests Game Development and Digital Art Undergraduate student in Computer Science at the Federal University of Campina Grande (UFCG).
As a pragmatic software engineer, my goal is to deliver reliable, high-value solutions to end users, regardless of the technologies involved. I believe that exceptional products are born from the synergy of flexible teams, clear communication, and decisions strictly guided by solid metrics and tangible impacts.</description><content>
&lt;div id="profile">
&lt;img
id="github-photo"
src="https://github.com/dougdomingos.png"
width="250"
height="250"
alt="GitHub profile picture" />
&lt;div class="profile__info">
&lt;div class="profile__info-badge">
&lt;span class="profile__badge-label">Name&lt;/span>
&lt;span>Douglas Domingos&lt;/span>
&lt;/div>
&lt;div class="profile__info-badge">
&lt;span class="profile__badge-label">Location&lt;/span>
&lt;span>Campina Grande, PB - Brazil&lt;/span>
&lt;/div>
&lt;div class="profile__info-badge">
&lt;span class="profile__badge-label">Education&lt;/span>
&lt;span>B.Sc Candidate in Computer Science&lt;/span>
&lt;/div>
&lt;div class="profile__info-badge">
&lt;span class="profile__badge-label">Skills&lt;/span>
&lt;span>Frontend, Backend, DevOps, Cloud&lt;/span>
&lt;/div>
&lt;div class="profile__info-badge">
&lt;span class="profile__badge-label">Interests&lt;/span>
&lt;span>Game Development and Digital Art&lt;/span>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;p>Undergraduate student in Computer Science at the Federal University of Campina
Grande (UFCG).&lt;/p>
&lt;p>As a pragmatic software engineer, my goal is to deliver reliable, high-value
solutions to end users, regardless of the technologies involved. I believe that
exceptional products are born from the synergy of flexible teams, clear
communication, and decisions strictly guided by solid metrics and tangible
impacts.&lt;/p>
&lt;p>Behind the pragmatism, however, lies the soul of an artist. I&amp;rsquo;m fascinated by
how the experiences and creativity of a team are translated into the
problem-solving process. For me, the true beauty of software architecture lies
in the way teams extract the most out of the available resources to build
incredible user experiences.&lt;/p>
&lt;p>Beyond DevOps and Infrastructure, I have a strong interest in Pixel Art and Game
Development. After all, creating games is what brought me into programming, and
this passion is still alive, more than ever. Today, I invest part of my free
time in traditional and digital art, as a way to express what can&amp;rsquo;t be contained
within the boundaries of an IDE.&lt;/p>
&lt;p>With plans to debut as an Indie GameDev at some point in the future, I&amp;rsquo;m
convinced that my technical background, alongside my artistic side, will merge
into a genuine identity that will be etched into every story I plan to tell.&lt;/p></content></item><item><title>Personal projects</title><link>https://dougdomingos.netlify.app/projects/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dougdomingos.netlify.app/projects/</guid><description> CaringHands An web platform that allows families and caregivers to manage medications and medical exams for their patients with much more simplicity and convenience
#react#tailwind-css#spring-boot#postgresql#aws-ec2#github-actions Image Filters An image processing application focused on performance, with multiple filters (e.g., Binarization, Sobel, Gaussian Blur) implemented using only built-in features of Go&amp;#39;s standard library.
#go#concurrent-programming#pixel-manipulation#study-project Expense Tracker API REST API built with Spring Boot and JWT Authentication for finance management
#spring-boot#spring-security#oauth2#jwt-auth#postgresql#swagger-ui</description><content>
&lt;div id="projects">
&lt;div class="project-card">
&lt;div class="project-card__body">
&lt;div class="project-card__banner-container">
&lt;img
class="project-card__banner"
src="https://dougdomingos.netlify.app/images/caring-hands.png"
alt="CaringHands banner"
loading="lazy" />
&lt;/div>
&lt;div class="project-card__content">
&lt;div class="project-card__info">
&lt;span class="project-card__title" translate="no">
CaringHands
&lt;/span>
&lt;p class="project-card__description">An web platform that allows families and caregivers to manage medications and medical exams for their patients with much more simplicity and convenience&lt;/p>
&lt;/div>
&lt;ul class="project-card__tags">
&lt;li class="project-card__tag" translate="no">#react&lt;/li>&lt;li class="project-card__tag" translate="no">#tailwind-css&lt;/li>&lt;li class="project-card__tag" translate="no">#spring-boot&lt;/li>&lt;li class="project-card__tag" translate="no">#postgresql&lt;/li>&lt;li class="project-card__tag" translate="no">#aws-ec2&lt;/li>&lt;li class="project-card__tag" translate="no">#github-actions&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>
&lt;/div>
&lt;a class="project-card project-card--clickable" href="https://github.com/dougdomingos/image-filters/">
&lt;div class="project-card__body">
&lt;div class="project-card__content">
&lt;div class="project-card__info">
&lt;span class="project-card__title" translate="no">
Image Filters
&lt;/span>
&lt;p class="project-card__description">An image processing application focused on performance, with multiple filters (e.g., Binarization, Sobel, Gaussian Blur) implemented using only built-in features of Go&amp;#39;s standard library.&lt;/p>
&lt;/div>
&lt;ul class="project-card__tags">
&lt;li class="project-card__tag" translate="no">#go&lt;/li>&lt;li class="project-card__tag" translate="no">#concurrent-programming&lt;/li>&lt;li class="project-card__tag" translate="no">#pixel-manipulation&lt;/li>&lt;li class="project-card__tag" translate="no">#study-project&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>
&lt;/a>
&lt;a class="project-card project-card--clickable" href="https://github.com/dougdomingos/expense-tracker">
&lt;div class="project-card__body">
&lt;div class="project-card__content">
&lt;div class="project-card__info">
&lt;span class="project-card__title" translate="no">
Expense Tracker API
&lt;/span>
&lt;p class="project-card__description">REST API built with Spring Boot and JWT Authentication for finance management&lt;/p>
&lt;/div>
&lt;ul class="project-card__tags">
&lt;li class="project-card__tag" translate="no">#spring-boot&lt;/li>&lt;li class="project-card__tag" translate="no">#spring-security&lt;/li>&lt;li class="project-card__tag" translate="no">#oauth2&lt;/li>&lt;li class="project-card__tag" translate="no">#jwt-auth&lt;/li>&lt;li class="project-card__tag" translate="no">#postgresql&lt;/li>&lt;li class="project-card__tag" translate="no">#swagger-ui&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>
&lt;/a>
&lt;/div></content></item><item><title>Professional Experiences</title><link>https://dougdomingos.netlify.app/experiences/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://dougdomingos.netlify.app/experiences/</guid><description>Dec 2025 – Present Backend Developer Laboratório de Computação Inteligente Aplicada (LACINA) Development of an experimental, multi-tenant ERP solution, built on top of Frappe Framework and ERPNext, implementing custom modules and integrating with third-party services. Python Frappe Framework ERPNext Web Development Feb 2025 – Nov 2025 Software Engineer Intern (R&amp;amp;D) NuFuturo UFCG Development of a web service for document analysis and information extraction with Python and Clojure, integrating LLMs, OCR and natural language processing techniques to enhance data processing capabilities, as well as managing cloud infrastructure on AWS for scalable deployments.</description><content>
&lt;div class="experience-timeline">
&lt;div class="timeline-row">
&lt;div class="experience-card">
&lt;div class="timespan is-current">
&lt;time>Dec 2025 – Present&lt;/time>
&lt;/div>
&lt;div class="experience-card__header">
&lt;h3 class="experience-card__title">Backend Developer&lt;/h3>
&lt;span class="experience-card__company">Laboratório de Computação Inteligente Aplicada (LACINA)&lt;/span>
&lt;/div>
&lt;p class="experience-card__description">
Development of an experimental, multi-tenant ERP solution, built on top of Frappe Framework and ERPNext, implementing custom modules and integrating with third-party services.
&lt;/p>
&lt;ul class="experience-card__tags">
&lt;li class="experience-card__tag">Python&lt;/li>
&lt;li class="experience-card__tag">Frappe Framework&lt;/li>
&lt;li class="experience-card__tag">ERPNext&lt;/li>
&lt;li class="experience-card__tag">Web Development&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>
&lt;div class="timeline-row">
&lt;div class="experience-card">
&lt;div class="timespan ">
&lt;time>Feb 2025 – Nov 2025&lt;/time>
&lt;/div>
&lt;div class="experience-card__header">
&lt;h3 class="experience-card__title">Software Engineer Intern (R&amp;amp;D)&lt;/h3>
&lt;span class="experience-card__company">NuFuturo UFCG&lt;/span>
&lt;/div>
&lt;p class="experience-card__description">
Development of a web service for document analysis and information extraction with Python and Clojure, integrating LLMs, OCR and natural language processing techniques to enhance data processing capabilities, as well as managing cloud infrastructure on AWS for scalable deployments.
&lt;/p>
&lt;ul class="experience-card__tags">
&lt;li class="experience-card__tag">Python&lt;/li>
&lt;li class="experience-card__tag">Clojure&lt;/li>
&lt;li class="experience-card__tag">LangChain&lt;/li>
&lt;li class="experience-card__tag">Large Language Models&lt;/li>
&lt;li class="experience-card__tag">Optical Character Recognition&lt;/li>
&lt;li class="experience-card__tag">Amazon Web Services&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>
&lt;div class="timeline-row">
&lt;div class="experience-card">
&lt;div class="timespan ">
&lt;time>Dec 2023 - Jan 2025&lt;/time>
&lt;/div>
&lt;div class="experience-card__header">
&lt;h3 class="experience-card__title">Linux System Administrator&lt;/h3>
&lt;span class="experience-card__company">Guardians UFCG&lt;/span>
&lt;/div>
&lt;p class="experience-card__description">
Mainentance of a custom Linux environment used for coding exams in the university&amp;#39;s computer labs, ensuring system stability and cheating prevention.
&lt;/p>
&lt;ul class="experience-card__tags">
&lt;li class="experience-card__tag">Linux&lt;/li>
&lt;li class="experience-card__tag">System Administration&lt;/li>
&lt;li class="experience-card__tag">Bash&lt;/li>
&lt;li class="experience-card__tag">Shell Scripting&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>
&lt;/div></content></item></channel></rss>