<?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>DEV Community: Karolis Šarapnickis</title>
    <description>The latest articles on DEV Community by Karolis Šarapnickis (@karolis_sh).</description>
    <link>https://dev.to/karolis_sh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F202678%2Fc5b102e7-da70-4729-99b9-9962999f5ff3.jpg</url>
      <title>DEV Community: Karolis Šarapnickis</title>
      <link>https://dev.to/karolis_sh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/karolis_sh"/>
    <language>en</language>
    <item>
      <title>A Practical Shell Scripting Introduction for JavaScript Engineers</title>
      <dc:creator>Karolis Šarapnickis</dc:creator>
      <pubDate>Sat, 06 Nov 2021 13:53:10 +0000</pubDate>
      <link>https://dev.to/karolis_sh/a-practical-shell-scripting-introduction-for-javascript-engineers-2fbo</link>
      <guid>https://dev.to/karolis_sh/a-practical-shell-scripting-introduction-for-javascript-engineers-2fbo</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Compact rundown though shell basics focusing on practical use cases in JavaScript projects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When developing scripts, I always had a tendency to go for Node.js instead of shell, even for relatively simple things.&amp;nbsp;Shell scripting has always been challenging for me, until I dedicated some time in learning the basics. Now I understand that, when used correctly, shell scripts can really simplify the logic.&amp;nbsp;So, here are some core concepts and&amp;nbsp;practical&amp;nbsp;use cases&amp;nbsp;that you can learn and hopefully apply in your projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Essential Commands
&lt;/h2&gt;

&lt;p&gt;Here is a list of some of the more common shell commands that you'd encounter in JS projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;echo&lt;/code&gt; - prints text terminal window (e.g., &lt;code&gt;echo Build is complete!&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Files and folders:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mkdir&lt;/code&gt; creates directories (e.g. create directories recursively &lt;code&gt;mkdir -p dist/code&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rm&lt;/code&gt; - removes files or directories (e.g. forcibly and recursively remove &lt;code&gt;dist&lt;/code&gt; directory - &lt;code&gt;rm -rf dist&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cp&lt;/code&gt; copy files and directories (e.g., &lt;code&gt;cp -f dist/404/index.html dist/404.html&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mv&lt;/code&gt; move or rename files or directories (e.g., &lt;code&gt;mv -f gen/robots.txt gen/manifest.json dist&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tar&lt;/code&gt; - archiving utility (e.g., create a gzipped archive and write it to a file - &lt;code&gt;tar -czf bundle.tar.gz dist/&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many more commands (&lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;cd&lt;/code&gt;, &lt;code&gt;cat&lt;/code&gt;, etc.), but when encountered you can look the up in the manual pages (e.g., &lt;code&gt;man tar&lt;/code&gt;). There's also a very nifty community-driven tool called &lt;a href="https://tldr.sh/"&gt;tldr&lt;/a&gt;, that aims to simplify the beloved&amp;nbsp;&lt;a href="https://en.wikipedia.org/wiki/Man_page"&gt;man pages&lt;/a&gt;&amp;nbsp;with practical examples (e.g., &lt;code&gt;tldr tar&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Essential Operators
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Command Chaining Operators
&lt;/h3&gt;

&lt;p&gt;Usage of a single command is rarely enough, so here some chaining operators:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; (the AND operator) - executes second command only if the first one &lt;strong&gt;succeeds&lt;/strong&gt; (e.g., &lt;code&gt;yarn &amp;amp;&amp;amp; yarn build &amp;amp;&amp;amp; yarn publish&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;;&lt;/code&gt; (the semicolon operator) - runs several commands, despite if previous one succeeded or not (e.g., &lt;code&gt;yarn build &amp;amp;&amp;amp; mv -f gen/sitemap.xml dist; echo Build done!&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many more shell operators, but these and &lt;code&gt;npm-run-all&lt;/code&gt; should cover the majority of your chaining needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Output &lt;code&gt;&amp;gt;&lt;/code&gt; and Output Append &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; Operators
&lt;/h3&gt;

&lt;p&gt;Both, output &lt;code&gt;&amp;gt;&lt;/code&gt; and output append &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; operators redirects content to a destination, but only &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; appends to the target. E.g., creating a &lt;code&gt;.env&lt;/code&gt; file in your CI pipeline:&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; &lt;span class="s2"&gt;"PORT=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PRODUCTION_PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .env
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"API_URL=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PRODUCTION_API_URL&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Command Substitution
&lt;/h2&gt;

&lt;p&gt;Command substitution is a mechanism by which the shell performs a given set of commands and then exchanges their output in the place of the commands. E.g., combine Node.js script evaluation and command substitution to have JavaScript output in shell 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;echo &lt;/span&gt;Version - &lt;span class="si"&gt;$(&lt;/span&gt;node &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"console.log(require('./package.json').version)"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conditional Statements
&lt;/h2&gt;

&lt;p&gt;Just like JavaScript, shell scripts can have &lt;code&gt;if&lt;/code&gt; statements. They can be written as both multi and single line statements. E.g., performing an optimized &lt;code&gt;yarn&lt;/code&gt; install for CI environment only:&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="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$CI&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;yarn &lt;span class="nt"&gt;--cache-folder&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/.yarn &lt;span class="nt"&gt;--prefer-offline&lt;/span&gt; &lt;span class="nt"&gt;--frozen-lockfile&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;yarn
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Environment Variables
&lt;/h2&gt;

&lt;p&gt;Environment variables are a common way of passing dynamically configurable values. Here are some use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Configurable environment values with defaults&lt;/strong&gt; - e.g., optional &lt;code&gt;PORT&lt;/code&gt; environment variable with defaults from npm config:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"config"&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;"port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1234&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;"srcipts"&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;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"serve -l ${PORT:-$npm_package_config_port}"&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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reusing &lt;code&gt;.env&lt;/code&gt; file in shell sessions&lt;/strong&gt; - e.g., a localhost release script for Lerna project that loads &lt;code&gt;.env&lt;/code&gt;, and performs some necessary checks:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; .env&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt; +a

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$GH_TOKEN&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚨 Missing GH_TOKEN env variable"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;npm &lt;span class="nb"&gt;whoami&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🚨 Not logged in to npm"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git status &lt;span class="nt"&gt;--porcelain&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;git checkout main &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git pull &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; yarn release
&lt;span class="k"&gt;else
    &lt;/span&gt;git status &lt;span class="nt"&gt;--porcelain&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🧹 Working directory not clean"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Shell scripting is a powerful and elegant way to perform some common operations. Dedicate some time and give it a proper try - it's a versatile skill to have at your tool belt. This blog post scratches only the surface of what's possible, so here are some resources for further reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dylanaraps/pure-bash-bible"&gt;github.com/dylanaraps/pure-bash-bible&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://mywiki.wooledge.org/BashGuide"&gt;mywiki.wooledge.org/BashGuide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>bash</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to Manage Multiple SSH Keys for GitHub, GitLab, etc.</title>
      <dc:creator>Karolis Šarapnickis</dc:creator>
      <pubDate>Sun, 12 Sep 2021 17:15:56 +0000</pubDate>
      <link>https://dev.to/karolis_sh/how-to-manage-multiple-ssh-keys-for-github-gitlab-etc-4f98</link>
      <guid>https://dev.to/karolis_sh/how-to-manage-multiple-ssh-keys-for-github-gitlab-etc-4f98</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Short tutorial on how to structure projects and setup multi-user Git SSH workflow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When using a single machine with multiple git accounts, you might encounter some obstacles with SSH setup. The easiest way to achieve a multi-user setup is to structure git repositories by respective directories, e.g.:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;projects
├── work
│   ├── enterprise-fiz-buz
│   └── ...
└── personal
    ├── karolis.sh
    └── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This short tutorial will help you set up SHH for such workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating a new SSH key
&lt;/h2&gt;

&lt;p&gt;To generate a new SSH key, use the provided template script (or check the &lt;a href="https://docs.github.com/en/github/authenticating-to-github/connecting-to-github-with-ssh"&gt;docs&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;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;EMAIL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;karolis.sarapnickis@work.com
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SSH_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.ssh/id_rsa_work
ssh-keygen &lt;span class="nt"&gt;-t&lt;/span&gt; rsa &lt;span class="nt"&gt;-b&lt;/span&gt; 4096 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nv"&gt;$EMAIL&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;$SSH_FILE&lt;/span&gt;
ssh-add &lt;span class="nt"&gt;-K&lt;/span&gt; &lt;span class="nv"&gt;$SSH_FILE&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🔽 PUBLIC KEY BELOW 🔽"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="nv"&gt;$SSH_FILE&lt;/span&gt;.pub
pbcopy &amp;lt; &lt;span class="nv"&gt;$SSH_FILE&lt;/span&gt;.pub
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What's left is to add the SSH key to your &lt;a href="https://github.com/settings/keys"&gt;GitHub&lt;/a&gt;/&lt;a href="https://gitlab.com/-/profile/keys"&gt;GitLab&lt;/a&gt;/etc. account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-user Git SSH setup
&lt;/h2&gt;

&lt;p&gt;The general idea is that you use specific &lt;a href="https://git-scm.com/docs/git-config.html#Documentation/git-config.txt-coresshCommand"&gt;ssh command&lt;/a&gt; based on your working directory with the help of &lt;a href="https://git-scm.com/docs/git-config.html#_includes"&gt;git includes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Update the &lt;code&gt;~/.gitconfig&lt;/code&gt; file:&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="nv"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="s"&gt;name = Karolis Šarapnickis&lt;/span&gt;
  &lt;span class="s"&gt;email = pastas.k@gmail.com&lt;/span&gt;
&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;core&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="s"&gt;sshCommand = ssh -i ~/.ssh/id_rsa&lt;/span&gt;
&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="s"&gt;default = current&lt;/span&gt;
&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;includeIf "gitdir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;~/projects/work/"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="s"&gt;path = ~/.gitconfig.work&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the &lt;code&gt;~/.gitconfig.work&lt;/code&gt; file:&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="nv"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="s"&gt;email = karolis.sarapnickis@work.com&lt;/span&gt;
&lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;core&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="s"&gt;sshCommand = ssh -i ~/.ssh/id_rsa_work&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! Appropriate SSH setup will be used based on directory location, no extra actions are needed.&lt;/p&gt;

</description>
      <category>ssh</category>
      <category>github</category>
      <category>gitlab</category>
    </item>
  </channel>
</rss>
