<?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: cerico</title>
    <description>The latest articles on DEV Community by cerico (@cerico).</description>
    <link>https://dev.to/cerico</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F126338%2F67d4389c-5ab3-472f-aa95-c760793fbdc6.jpeg</url>
      <title>DEV Community: cerico</title>
      <link>https://dev.to/cerico</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cerico"/>
    <language>en</language>
    <item>
      <title>Dokku's /var/lib/docker/overlay2 too big?</title>
      <dc:creator>cerico</dc:creator>
      <pubDate>Tue, 11 Jun 2024 08:16:46 +0000</pubDate>
      <link>https://dev.to/cerico/dokkus-varlibdockeroverlay2-too-big-3cf</link>
      <guid>https://dev.to/cerico/dokkus-varlibdockeroverlay2-too-big-3cf</guid>
      <description>&lt;h2&gt;
  
  
  Dokku's /var/lib/docker/overlay2 too big?
&lt;/h2&gt;

&lt;p&gt;One of the frustrating things about Dokku, is that pushes often report as succesful when they haven't been. The most obvious example is when it didn't trigger a build (see last post for more on that). But another one is if you're out of disk space, and dokku fills up the /var/lib/overlay2 directory with a lot of images. Dokku's own prune command is very conservative and doesn't make much of an impact at all. And deleting anything from this directory is an absolute no no.&lt;/p&gt;

&lt;p&gt;Freeing up space more effectively can be done with&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system prune &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This freed me up 21G of space. But it is going to fill up again pretty soon, so this is best set up as a cron job with the -f flag to stop it requesting for confirmation&lt;/p&gt;

&lt;p&gt;Hit crontab -e and add the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;56 10 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; docker system prune &lt;span class="nt"&gt;-af&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this should keep your /var/lib/docker/overlay2 folder in check&lt;/p&gt;

</description>
      <category>dokku</category>
      <category>git</category>
    </item>
    <item>
      <title>If your dokku push doesn't trigger a build</title>
      <dc:creator>cerico</dc:creator>
      <pubDate>Tue, 11 Jun 2024 03:56:36 +0000</pubDate>
      <link>https://dev.to/cerico/dokku-kdh</link>
      <guid>https://dev.to/cerico/dokku-kdh</guid>
      <description>&lt;h2&gt;
  
  
  If your dokku push doesn't trigger a build
&lt;/h2&gt;

&lt;p&gt;Dokku is a great cost-effective way to host your Rails apps, and is well documented elsewhere. I followed the instructions at &lt;a href="https://marketplace.digitalocean.com/apps/dokku"&gt;https://marketplace.digitalocean.com/apps/dokku&lt;/a&gt; to create my dokku droplet. But there are a couple of caveats. The first is you'll need to increase swap size on your VM for dokku to work. I created a zsh/bash function to do that&lt;/p&gt;

&lt;h3&gt;
  
  
  Increase Swap Size
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bump &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;sudo install&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; root &lt;span class="nt"&gt;-g&lt;/span&gt; root &lt;span class="nt"&gt;-m&lt;/span&gt; 0600 /dev/null /swapfile
    &lt;span class="nb"&gt;sudo dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/swapfile &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1k &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2048k
    &lt;span class="nb"&gt;sudo &lt;/span&gt;mkswap /swapfile
    &lt;span class="nb"&gt;sudo &lt;/span&gt;swapon /swapfile
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"/swapfile       swap    swap    auto      0       0"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/fstab
    &lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-w&lt;/span&gt; vm.swappiness&lt;span class="o"&gt;=&lt;/span&gt;10
    &lt;span class="nb"&gt;echo &lt;/span&gt;vm.swappiness &lt;span class="o"&gt;=&lt;/span&gt; 10 | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/sysctl.conf
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create Application
&lt;/h3&gt;

&lt;p&gt;You also need to create your application before you can deploy it. There are a number of things to do here, so i also combined them into one function&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;newapp &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;email&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cityguessr@skiff.com"&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ol14.cc"&lt;/span&gt;
    dokku apps:create &lt;span class="nv"&gt;$1&lt;/span&gt;
    dokku postgres:create &lt;span class="nv"&gt;$1db&lt;/span&gt;
    dokku postgres:link &lt;span class="nv"&gt;$1db&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt;
    dokku domains:set &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt;.&lt;span class="nv"&gt;$domain&lt;/span&gt;
    dokku letsencrypt:set &lt;span class="nv"&gt;$1&lt;/span&gt; email &lt;span class="nv"&gt;$email&lt;/span&gt;
    dokku letsencrypt:enable &lt;span class="nv"&gt;$1&lt;/span&gt;
    dokku letsencrypt:auto-renew
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll need to add the letsencrypt dokku plugin&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dokku plugin:install letsencrypt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and you can add a remote&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;g remote add dokku dokku@ol14.cc:kiseljak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and it will push your app and build it&lt;/p&gt;

&lt;h3&gt;
  
  
  But will it? (Push doesn't trigger a build)
&lt;/h3&gt;

&lt;p&gt;I couldn't get this to work consistently, and I couldn't find much helpful on the web at all. But I was able to trigger a build from the dokku server itself with the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dokku ps:rebuild kiseljak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I knew the app was fine, but doing a git push wasn't triggering a rebuild. The feedback from the git push isn't that helpful as it reports a successful push but no build is triggered&lt;/p&gt;

&lt;h3&gt;
  
  
  The solution
&lt;/h3&gt;

&lt;p&gt;The solution that worked best for me was to create a global post-receive hook that will trigger the build automatically&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;☁  dokku@ol14:~ ➜ &lt;span class="nb"&gt;cat&lt;/span&gt; ~/.config/git/hooks/post-receive
&lt;span class="nv"&gt;REPO_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; dokku &amp;amp;&amp;gt; /dev/null
&lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;DOKKU_ROOT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/home/dokku"&lt;/span&gt; dokku git-hook &lt;span class="nv"&gt;$REPO_NAME&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your hooksPath may be different, you can check or set it in your gitconfig&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;☁  dokku@ol14:~ ➜ &lt;span class="nb"&gt;cat&lt;/span&gt; ~/.gitconfig | &lt;span class="nb"&gt;grep &lt;/span&gt;core &lt;span class="nt"&gt;-A2&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;core]
        editor &lt;span class="o"&gt;=&lt;/span&gt; vi
        hooksPath &lt;span class="o"&gt;=&lt;/span&gt; ~/.config/git/hooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we return to the pre-recieve hook, this sets a git-hook of the repository name, which gets set on app creation (so will work for all newly created applications on the dokku server). On git push, the post-recieve hook is activated and the build process starts&lt;/p&gt;

&lt;h3&gt;
  
  
  Github Action
&lt;/h3&gt;

&lt;p&gt;Naturally, you'll want this to work on a github action rather than pushing directly to dokku. Here is an example of a working action&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="s"&gt;☁  brew@kelso:kiseljak cat .github/workflows/dokku.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dokku"&lt;/span&gt;

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kiseljak&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-22.04&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cloning repo&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Push to dokku&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dokku/github-action@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt;
          &lt;span class="na"&gt;git_remote_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ssh://dokku@64.23.226.251:22/~/kiseljak"&lt;/span&gt;
          &lt;span class="na"&gt;ssh_private_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_PRIVATE_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The git_remote_url line is the most important to get right here. I could not get this to work via domain, only via ip. I'm not sure if this is someting to do with ipv6 or not, but to get working via domain may need extra work. If you're using dokku it means you're using your own vps, so you will have a static ip to use here&lt;/p&gt;

&lt;h3&gt;
  
  
  Dockerfile caveat
&lt;/h3&gt;

&lt;p&gt;One other thing whih sometimes seems to get in the way of dokku is the Dockerfile that Rails provides by default. In some applications this seemed to be a problem and in others it wasn't. If you don't nee the Dockerfile, just rename to Dockerfile.orig&lt;/p&gt;

</description>
      <category>git</category>
      <category>dokku</category>
      <category>rails</category>
    </item>
    <item>
      <title>Viewing and finding images more easily</title>
      <dc:creator>cerico</dc:creator>
      <pubDate>Wed, 20 Mar 2024 10:06:01 +0000</pubDate>
      <link>https://dev.to/cerico/viewing-and-finding-images-more-easily-2bld</link>
      <guid>https://dev.to/cerico/viewing-and-finding-images-more-easily-2bld</guid>
      <description>&lt;h2&gt;
  
  
  Viewing and finding images more easily in filesystem
&lt;/h2&gt;

&lt;p&gt;If you have lots of images scattered across different locations in your filesystem, the spotlight search isn't necessarily the most user friendly way of finding the one you're looking for if you don't know the name of the file.&lt;/p&gt;

&lt;p&gt;The small script below finds all images relative to the point the function is run, creates a html page and populates it with each image it finds. It then spins up a webserver and opens the newly created page. Once the command is terminated the temporary html page is removed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pics &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.zsh/templates/html"&lt;/span&gt;
  &lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;list&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.[jp]&lt;span class="k"&gt;*&lt;/span&gt;g&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;page&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"000.html"&lt;/span&gt;
    &lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'_delete_temp_page $page'&lt;/span&gt; INT
    &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'$d'&lt;/span&gt; &lt;span class="nv"&gt;$template&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'$d'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$page&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;do
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;img src=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;./&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$page&lt;/span&gt;
    &lt;span class="k"&gt;done
    &lt;/span&gt;&lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 2 &lt;span class="nv"&gt;$template&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$page&lt;/span&gt;
    _webserver &lt;span class="nv"&gt;$page&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Firstly we create a blank html page in the templates directory, shown later in this post. Secondly we set the Internal Field Separator to a newline character instead of the default space. We need to do this because it allows for filenames that contain blank spaces, something commonplace on a Mac.&lt;/p&gt;

&lt;p&gt;Next we create a list of all images files relative to the current location, and create a page variable of &lt;code&gt;000.html&lt;/code&gt;, though this could be renamed to something more abstract as it will be destroyed. Or a check could be added in here to return if a file with that name already exists, but I've not included that here.&lt;/p&gt;

&lt;p&gt;Next we set a &lt;code&gt;trap&lt;/code&gt; to listen for the INT signal, generally CTRL-C or whatever escape character is pressed, this will then run the &lt;code&gt;_delete_temp_page&lt;/code&gt; function detailed below, passing it the newly created &lt;code&gt;000.html&lt;/code&gt; page.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;sed&lt;/code&gt; command removes the last two lines from the template file (the closing body and html tags), and then we iterate through the list of found images creating an image tag in the html for each entry. Finally we use &lt;code&gt;tail&lt;/code&gt; to add the last two lines from the template file back in. We then spin up a webserver with the &lt;code&gt;_webserver&lt;/code&gt; function&lt;/p&gt;

&lt;h3&gt;
  
  
  _delete_temp_page
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;_delete_temp_page &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Stopping web server..."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt;
    &lt;span class="nb"&gt;trap&lt;/span&gt; - INT
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we remove the file that is passed to the function, in this case 000.html, and disable the trap. Otherwise ctrl-c will continue to invoke the function whenever pressed&lt;/p&gt;

&lt;h3&gt;
  
  
  _webserver
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;_webserver &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    browser-sync start &lt;span class="nt"&gt;--server&lt;/span&gt; &lt;span class="nt"&gt;--startPath&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;--port&lt;/span&gt; 6375 &lt;span class="nt"&gt;--browser&lt;/span&gt; &lt;span class="s2"&gt;"safari"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple one liner to open the page in Safari&lt;/p&gt;

&lt;h3&gt;
  
  
  the template html
&lt;/h3&gt;

&lt;p&gt;Here we create a template html page and story it in the templates dir inside zsh&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt; cat ~/.zsh/templates/html
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100vw&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="m"&gt;32px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing exciting here, as we saw above the script copies this file, uses sed to remove the last two lines before adding the image entries, and then using tail to add the last two lines in. Due to its generic nature we can reuse this &lt;code&gt;html&lt;/code&gt; file in other contexts too.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>git</category>
    </item>
    <item>
      <title>Using a default Makefile</title>
      <dc:creator>cerico</dc:creator>
      <pubDate>Mon, 15 Jan 2024 06:55:55 +0000</pubDate>
      <link>https://dev.to/cerico/using-a-default-makefile-59a</link>
      <guid>https://dev.to/cerico/using-a-default-makefile-59a</guid>
      <description>&lt;h2&gt;
  
  
  Setting up a default Makefile with wildcard rule
&lt;/h2&gt;

&lt;p&gt;When setting up a new project, I like to have a default Makefile that I can use to run common tasks. This is a simple Makefile that I use to run common tasks. A project might have different ways of launching parts of the application, and standardising and centralising these commands is a good way of keeping things simple and consistent. It is also self-documenting, as the &lt;code&gt;tldr&lt;/code&gt; target lists all the available targets by default. Lets take a look at my default Makefile, and then explore easily targets via a shell function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;tldr&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;Available commands
        &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;------------------&lt;/span&gt;
        &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'^[[:alpha:]][^:[:space:]]*:'&lt;/span&gt; Makefile | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;':'&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 1 | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/^/make /'&lt;/span&gt;
&lt;span class="nl"&gt;%&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="p"&gt;@$(&lt;/span&gt;MAKE&lt;span class="p"&gt;)&lt;/span&gt; tldr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we can see, there are only two commands defined. The first is &lt;code&gt;tldr&lt;/code&gt;, which lists all the available commands. The second is &lt;code&gt;%&lt;/code&gt;, which is a wildcard that matches any command that is not defined. This is used to print the &lt;code&gt;tldr&lt;/code&gt; command if an invalid command is entered.&lt;/p&gt;

&lt;p&gt;The tldr rule prints out all the commands in the Makefile. It does this by using &lt;code&gt;grep&lt;/code&gt; to find all the lines that start with a letter, followed by a colon. It then uses &lt;code&gt;cut&lt;/code&gt; to extract the first field, which is the command name. It then uses &lt;code&gt;sort&lt;/code&gt; to sort the commands, and finally uses &lt;code&gt;sed&lt;/code&gt; to add the &lt;code&gt;make&lt;/code&gt; command to the start of each line. The output looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;☁  brew@kelso&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;projects/making  ➜ make tldr&lt;/span&gt;
&lt;span class="err"&gt;Available&lt;/span&gt; &lt;span class="err"&gt;commands&lt;/span&gt;
&lt;span class="err"&gt;------------------&lt;/span&gt;
&lt;span class="err"&gt;make&lt;/span&gt; &lt;span class="err"&gt;tldr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As there is only one rule defined, the wildcard rule is used to print the &lt;code&gt;tldr&lt;/code&gt; command. Same thing happens if you enter an invalid command, instead of erroring out, it prints the &lt;code&gt;tldr&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;☁  brew@kelso&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;projects/making  ➜ make nonsense&lt;/span&gt;
&lt;span class="err"&gt;Available&lt;/span&gt; &lt;span class="err"&gt;commands&lt;/span&gt;
&lt;span class="err"&gt;------------------&lt;/span&gt;
&lt;span class="err"&gt;make&lt;/span&gt; &lt;span class="err"&gt;tldr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the addmake function to create or add to the Makefile
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;addmake &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; Makefile &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;cp&lt;/span&gt; ~/.zsh/templates/Makefile &lt;span class="nb"&gt;.&lt;/span&gt;
  &lt;span class="k"&gt;fi
  if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$1&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;make tldr
    &lt;span class="k"&gt;return
  fi
  &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="s2"&gt;"target?Enter target: "&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;$target&lt;/span&gt; &lt;span class="o"&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;"Error: A file or directory named '&lt;/span&gt;&lt;span class="nv"&gt;$target&lt;/span&gt;&lt;span class="s2"&gt;' already exists."&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;1
  &lt;span class="k"&gt;fi
  if &lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="nv"&gt;$target&lt;/span&gt;&lt;span class="s2"&gt;:"&lt;/span&gt; Makefile
    &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Error: Target '&lt;/span&gt;&lt;span class="nv"&gt;$target&lt;/span&gt;&lt;span class="s2"&gt;' already exists in the Makefile."&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;1
  &lt;span class="k"&gt;fi&lt;/span&gt;
  &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$2&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;recipe&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="s2"&gt;"recipe?Enter recipe: "&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$target&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\n\t&lt;/span&gt;&lt;span class="nv"&gt;$recipe&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; Makefile
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Success: Target '&lt;/span&gt;&lt;span class="nv"&gt;$target&lt;/span&gt;&lt;span class="s2"&gt;' added to Makefile&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  make tldr
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets run through the function. First, we check if a Makefile exists in the current directory. If it doesn't, we copy the default Makefile shown above, which is stored in &lt;code&gt;~/.zsh/templates/Makefile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, we check if the first argument is empty. If it is, we run &lt;code&gt;make&lt;/code&gt; to print the &lt;code&gt;tldr&lt;/code&gt; command, listing available commands, and then exit.&lt;/p&gt;

&lt;p&gt;If we run the addmake function with one argument, it will create a new target with the argument and then prompt us for the corresponding recipe. If we run it with two arguments, it will create a new target with the first argument and the recipe with the second argument, before running the tldr target to list all the available commands, including the newly added target.&lt;/p&gt;

&lt;p&gt;It also checks to make sure the target doesn't have the same name as a file or directory at the top level of the project, which make doesn't seem to like. It also checks to make sure the target doesn't already exist in the Makefile.&lt;/p&gt;

&lt;p&gt;Lets take a look at the function in action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;☁  brew@kelso:projects/making ➜ addmake
Available commands
&lt;span class="nt"&gt;------------------&lt;/span&gt;
make tldr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;☁  brew@kelso&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;projects/making  ➜ addmake hello "@echo hello world"&lt;/span&gt;

&lt;span class="nl"&gt;Success&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Target 'hello' added to Makefile&lt;/span&gt;

&lt;span class="err"&gt;Available&lt;/span&gt; &lt;span class="err"&gt;commands&lt;/span&gt;
&lt;span class="err"&gt;------------------&lt;/span&gt;
&lt;span class="err"&gt;make&lt;/span&gt; &lt;span class="err"&gt;hello&lt;/span&gt;
&lt;span class="err"&gt;make&lt;/span&gt; &lt;span class="err"&gt;tldr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;☁  brew@kelso&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;projects/making  ➜ addmake goodbye&lt;/span&gt;
&lt;span class="nl"&gt;Enter recipe&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;"@echo see you in the next episode"&lt;/span&gt;

&lt;span class="nl"&gt;Success&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Target 'goodbye' added to Makefile&lt;/span&gt;

&lt;span class="err"&gt;Available&lt;/span&gt; &lt;span class="err"&gt;commands&lt;/span&gt;
&lt;span class="err"&gt;------------------&lt;/span&gt;
&lt;span class="err"&gt;make&lt;/span&gt; &lt;span class="err"&gt;goodbye&lt;/span&gt;
&lt;span class="err"&gt;make&lt;/span&gt; &lt;span class="err"&gt;hello&lt;/span&gt;
&lt;span class="err"&gt;make&lt;/span&gt; &lt;span class="err"&gt;tldr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;☁  brew@kels&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;projects/making ➜ addmake hello&lt;/span&gt;
&lt;span class="nl"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Target 'hello' already exists in the Makefile.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;☁  brew@kels&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="nf"&gt;projects/making ➜ addmake images&lt;/span&gt;
&lt;span class="nl"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;A file or directory named 'images' already exists.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>make</category>
    </item>
    <item>
      <title>Improving display of unmerged commits</title>
      <dc:creator>cerico</dc:creator>
      <pubDate>Mon, 21 Aug 2023 04:34:23 +0000</pubDate>
      <link>https://dev.to/cerico/improving-display-of-unmerged-commits-2e72</link>
      <guid>https://dev.to/cerico/improving-display-of-unmerged-commits-2e72</guid>
      <description>&lt;h2&gt;
  
  
  Improving display of unmerged commits
&lt;/h2&gt;

&lt;p&gt;The purpose of the &lt;code&gt;unmerged&lt;/code&gt; function is to show only unmerged commits from all branches. Lets take a look at the output, and then delve into the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Output
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2kpPjrei--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.io37.ch/images/unmerged2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2kpPjrei--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.io37.ch/images/unmerged2.jpg" alt="unmerged output" width="800" height="140"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the function is run from inside a repo it will return&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the number of unmerged commits&lt;/li&gt;
&lt;li&gt;how long ago the last commit was&lt;/li&gt;
&lt;li&gt;the commit message of the last commit&lt;/li&gt;
&lt;li&gt;the name of its branch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DgtTSgvs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.io37.ch/images/unmerged.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DgtTSgvs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.io37.ch/images/unmerged.png" alt="unmerged output" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the function is run from outside a repo it will check for repos in the current directory, and return the name of the repo and the same information as in the previous instance, but limited to the last two unmerged commits per repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Lets take a look at the code. We'll look at the main &lt;code&gt;unmerged&lt;/code&gt; function initially, and then the two helper functions it uses.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;unmerged&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;unmerged &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c"&gt;# List unmerged commits # ➜ unmerged 5&lt;/span&gt;
  &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="nt"&gt;-d&lt;/span&gt; .git &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;_unmerged_commits_across_repos
    &lt;span class="k"&gt;return
  fi
  &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;_default_branch&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;no&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;no&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;500 &lt;span class="c"&gt;# List most recent unmerged commit in each branch&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;branch &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;git branch &lt;span class="nt"&gt;--sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-authordate&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"* "&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="nv"&gt;$default&lt;/span&gt;&lt;span class="s2"&gt;$"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git log &lt;span class="nv"&gt;$default&lt;/span&gt;..&lt;span class="nv"&gt;$branch&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="nv"&gt;no&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-list &lt;span class="nt"&gt;--count&lt;/span&gt; &lt;span class="nv"&gt;$default&lt;/span&gt;..&lt;span class="nv"&gt;$branch&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git log &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="nv"&gt;$branch&lt;/span&gt; &lt;span class="nt"&gt;--pretty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;format:&lt;span class="s2"&gt;"%ar"&lt;/span&gt; &lt;span class="nt"&gt;--no-walk&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
      &lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git log &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="nv"&gt;$branch&lt;/span&gt; &lt;span class="nt"&gt;--pretty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;format:&lt;span class="s2"&gt;"%s"&lt;/span&gt; &lt;span class="nt"&gt;--no-walk&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$no&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$branch&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi
  done&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; -&lt;span class="nv"&gt;$no&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{first = $1; date = $2 " " $3 " " $4; last = $NF; message = substr($0, length($1 $2 $3 $4) + 5, length($0) - length($1 $2 $3 $4 $NF) - 5); printf "\033[0;32m%-3s \033[1;0m%-15s \033[0;32m%-52s \033[0;36m%s\n", first, date, message, last}'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets break down the above function step by step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; .git &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then
    &lt;/span&gt;_unmerged_commits_across_repos
    &lt;span class="k"&gt;return
fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If the current directory is not a git repository (i.e., it doesn't have a &lt;code&gt;.git&lt;/code&gt; directory), it calls the helper function &lt;code&gt;_unmerged_commits_across_repos&lt;/code&gt; and then exits.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;_default_branch&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This calls the helper function &lt;code&gt;_default_branch&lt;/code&gt; to determine the default branch of the repository.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;no&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;no&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;500
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If the function is called with an argument (e.g., &lt;code&gt;unmerged 10&lt;/code&gt;), it will use that number to limit the number of branches displayed. If not, it defaults to showing 500 branches, effectively showing all unmerged branches&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;branch &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;git branch &lt;span class="nt"&gt;--sort&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-authordate&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"* "&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="nv"&gt;$default&lt;/span&gt;&lt;span class="s2"&gt;$"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git log &lt;span class="nv"&gt;$default&lt;/span&gt;..&lt;span class="nv"&gt;$branch&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This loop goes through each branch in the repository, sorted by the author date in descending order. It excludes the default branch, and checks if there are any commits in the branch that are not in the default branch.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git log &lt;span class="nv"&gt;$default&lt;/span&gt;..&lt;span class="nv"&gt;$branch&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This checks if there are any commits in the branch that are not in the default branch.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;no&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-list &lt;span class="nt"&gt;--count&lt;/span&gt; &lt;span class="nv"&gt;$default&lt;/span&gt;..&lt;span class="nv"&gt;$branch&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git log &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="nv"&gt;$branch&lt;/span&gt; &lt;span class="nt"&gt;--pretty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;format:&lt;span class="s2"&gt;"%ar"&lt;/span&gt; &lt;span class="nt"&gt;--no-walk&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git log &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="nv"&gt;$branch&lt;/span&gt; &lt;span class="nt"&gt;--pretty&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;format:&lt;span class="s2"&gt;"%s"&lt;/span&gt; &lt;span class="nt"&gt;--no-walk&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$no&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$branch&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This prints the number of unmerged commits, the date of the last commit, its message, and the branch name. The &lt;code&gt;awk&lt;/code&gt; command at the end of the function is used to format the output and add some color to it. The output will show the number of unmerged commits in green, the date in default color, the commit message in green, and the branch name in cyan.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;_unmerged_commits_across_repos&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;_unmerged_commits_across_repos &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;.git &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="o"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;unmerged 2&lt;span class="si"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$output&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;local &lt;/span&gt;&lt;span class="nv"&gt;repo_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--show-toplevel&lt;/span&gt;&lt;span class="si"&gt;))&lt;/span&gt;
          &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'\e[36m'&lt;/span&gt;&lt;span class="nv"&gt;$repo_name&lt;/span&gt;
          &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"----------------"&lt;/span&gt;
          &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$output&lt;/span&gt;
        &lt;span class="k"&gt;fi&lt;/span&gt;
      &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;fi
  done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets break down the above function step by step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;/
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;.git &lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This loop iterates over all directories in the current directory.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;(&lt;/span&gt;
  &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;unmerged 2&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The code inside the parentheses runs in a subshell, which means it won't affect the current shell's environment. The script changes to the directory &lt;code&gt;$i&lt;/code&gt; and then calls the &lt;code&gt;unmerged&lt;/code&gt; to check for the 2 most recent unmerged commits.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$output&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;local &lt;/span&gt;&lt;span class="nv"&gt;repo_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--show-toplevel&lt;/span&gt;&lt;span class="si"&gt;))&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'\e[36m'&lt;/span&gt;&lt;span class="nv"&gt;$repo_name&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"----------------"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$output&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If the &lt;code&gt;unmerged&lt;/code&gt; function returns any output (indicating there are unmerged commits), the script gets the name of the repository using &lt;code&gt;git rev-parse --show-toplevel&lt;/code&gt; and then prints the repository name in cyan, followed by a separator, and the output from the &lt;code&gt;unmerged&lt;/code&gt; function.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;_default_branch&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;_default_branch &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; .git/refs/remotes/origin/HEAD &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;local &lt;/span&gt;&lt;span class="nv"&gt;branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"main"&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;branch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git symbolic-ref refs/remotes/origin/HEAD | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s@^refs/remotes/origin/@@'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;fi
   &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$branch&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function determines the default branch of the repository. It checks if the file &lt;code&gt;.git/refs/remotes/origin/HEAD&lt;/code&gt; exists. If it does, it uses the contents of that file to determine the default branch. If not, it defaults to &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>Why you should use a Makefile</title>
      <dc:creator>cerico</dc:creator>
      <pubDate>Fri, 02 Jun 2023 09:33:22 +0000</pubDate>
      <link>https://dev.to/cerico/why-you-should-use-a-makefile-1clf</link>
      <guid>https://dev.to/cerico/why-you-should-use-a-makefile-1clf</guid>
      <description>&lt;h2&gt;
  
  
  Why you should use a Makefile
&lt;/h2&gt;

&lt;p&gt;If you have a project or application where you need to run a lot of commands, npm, rake, bin, rails, etc, then you should consider using a Makefile as a front end utility. This has a few advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can run multiple commands at once&lt;/li&gt;
&lt;li&gt;All commands are launched with the same command, &lt;strong&gt;make&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;All commands are self-documenting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lets look at an example of a sample Makefile&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;tldr&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;Available commands
        &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;------------------&lt;/span&gt;
        &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'^[[:alpha:]][^:[:space:]]*:'&lt;/span&gt; Makefile | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;':'&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 1 | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/^/make /'&lt;/span&gt;
&lt;span class="nl"&gt;install&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        bundle &lt;span class="nb"&gt;install&lt;/span&gt;
        yarn &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--check-files&lt;/span&gt;
&lt;span class="nl"&gt;exchange_rates&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        ./bin/rake exchange_rates:refresh
&lt;span class="nl"&gt;subscription_plans&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rails runner &lt;span class="s1"&gt;'SubscriptionPlan.import_all_plans'&lt;/span&gt;
&lt;span class="nl"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        ./bin/init.js
&lt;span class="nl"&gt;storybook&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        yarn storybook
&lt;span class="nl"&gt;start&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we have a variety of commands that are all launched in different ways. With a Makefile, we can run them each of them with an easier to remember &lt;code&gt;make \&amp;lt;command\&amp;gt;&lt;/code&gt;. We can also see all the commands available with &lt;code&gt;make tldr&lt;/code&gt; or just &lt;code&gt;make&lt;/code&gt;. Any new commands added to the Makefile are automatically caught by the &lt;code&gt;make tldr&lt;/code&gt; command.&lt;/p&gt;

&lt;h2&gt;
  
  
  In action
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;☁  cericow@kelso:esplanade ➜ make
Available commands
&lt;span class="nt"&gt;------------------&lt;/span&gt;
make tldr
make &lt;span class="nb"&gt;install
&lt;/span&gt;make exchange_rates
make subscription_plans
make generate
make storybook
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the &lt;strong&gt;make&lt;/strong&gt; command picks up the first rule (&lt;strong&gt;tldr&lt;/strong&gt;) and runs it. The rule greps the file for available rules and lists them out in the format in which they should be run.&lt;/p&gt;

</description>
      <category>make</category>
    </item>
    <item>
      <title>Policing commit messages to conform to a semver-like standard</title>
      <dc:creator>cerico</dc:creator>
      <pubDate>Thu, 01 Jun 2023 05:01:41 +0000</pubDate>
      <link>https://dev.to/cerico/policing-commit-messages-to-conform-to-a-semver-like-standard-2bdi</link>
      <guid>https://dev.to/cerico/policing-commit-messages-to-conform-to-a-semver-like-standard-2bdi</guid>
      <description>&lt;h2&gt;
  
  
  Policing commit messages to conform to a semver-like standard
&lt;/h2&gt;

&lt;p&gt;I've been using a git hook to police commit messages for a while now to ensure that they conform to a semver-like standard. As someone thats prone to writing commit messages like "fixing stuff" or "more stuff" this has been a great way to enforce better commit messages. Lets look at the hook below.&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;# ~/.config/git/hooks/commit-msg&lt;/span&gt;

&lt;span class="c"&gt;#!/usr/bin/env zsh&lt;/span&gt;
&lt;span class="nb"&gt;declare&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&amp;lt; &lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;%%&lt;/span&gt;&lt;span class="s1"&gt;$'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="p"&gt;*&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; 20 &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Please enter a more informative commit message'&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="o"&gt;[[&lt;/span&gt; &lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;title&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 50 &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Please keep commit summary below 51 characters'&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="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$msg&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; wip:[[:space:]]&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$msg&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; fix:[[:space:]]&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$msg&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; feat:[[:space:]]&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$msg&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; feat!:[[:space:]]&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$msg&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; docs:[[:space:]]&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"your commit should begin with fix:, feat:, feat!:, docs:, or wip:"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"dont forget the colon, and the space after it"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"commits prefixed with wip must be squashed before submitting PR"&lt;/span&gt;
&lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This hook is written in zsh, but it should be easy to convert to bash if thats your preference. The hook is placed in &lt;code&gt;~/.config/git/hooks/commit-msg&lt;/code&gt; and is made executable with &lt;code&gt;chmod +x ~/.config/git/hooks/commit-msg&lt;/code&gt;. The hook is run after you write your commit message and save it. If the hook exits with a non-zero exit code, the commit is aborted. The hook checks that the commit message is between 20 and 50 characters, and that it begins with one of the following prefixes: wip:, fix:, feat:, feat!:, or docs:. This only affects the title or first line of the PR, the body can be any length.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;fix:&lt;/strong&gt; (0.0.1), &lt;strong&gt;feat:&lt;/strong&gt; (0.1.0), and &lt;strong&gt;feat!:&lt;/strong&gt; (1.0.0) conform to semver standards, and can be used with a github action like conventional-changelog-action to auto-update package.json, changelog.md, and create releases on github.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;docs:&lt;/strong&gt; signifes a change to documentation, and does not affect the version number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;wip:&lt;/strong&gt; is a special prefix that allows you to commit work in progress. These commits should be squashed before submitting a PR.&lt;/p&gt;

&lt;p&gt;Now if we try commiting with a message that doesn't conform to the above standards we get the following error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;☁  cerico@kelso:data-for-france ➜ &lt;span class="o"&gt;(&lt;/span&gt;kos-13-adding-data-for-strasbourg&lt;span class="o"&gt;)&lt;/span&gt; git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"im just fixing stuff"&lt;/span&gt;
your commit should begin with fix:, feat:, feat!:, docs:, or wip:
dont forget the colon, and the space after it
commits prefixed with wip must be squashed before submitting PR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above hook prevents commits which don't conform to the semver standard, but sometimes we might want to be able to add temporary "fixing stuff" type commits that aren't intended to make it into the main branch. The hook allows me us do so as long as the messages is prefixed with &lt;strong&gt;wip:&lt;/strong&gt;. When its time to submit a PR we can see clearly any commits that should be squashed or otherwise tidied up before submitting the PR. In a later post I'll show another function &lt;code&gt;ghpr&lt;/code&gt;, which will catch any wip commits before creating the PR, as well as filling in the title and body of the PR.&lt;/p&gt;

</description>
      <category>git</category>
    </item>
    <item>
      <title>Blocking commits on main with the pre-commit hook</title>
      <dc:creator>cerico</dc:creator>
      <pubDate>Wed, 31 May 2023 08:51:43 +0000</pubDate>
      <link>https://dev.to/cerico/blocking-commits-on-main-with-the-pre-commit-hook-36l1</link>
      <guid>https://dev.to/cerico/blocking-commits-on-main-with-the-pre-commit-hook-36l1</guid>
      <description>&lt;h2&gt;
  
  
  Blocking commits on main with the pre-commit hook
&lt;/h2&gt;

&lt;p&gt;While you can edit the settings on github to block commits on main, it's also possible to do it locally so you can prevent it from happening in the first place by using the pre-commit hook.&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;# ~/.config/git/hooks/pre-commit&lt;/span&gt;
&lt;span class="nv"&gt;branch&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 branch &lt;span class="nt"&gt;--show-current&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;commits&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 rev-list &lt;span class="nt"&gt;--all&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$branch&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$commits&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&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;"Commit on main branch is blocked, there are already existing commits."&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;p&gt;Any newly created repo will automatically have this hook in place. If you want to add it to an existing repo, you can copy the file to the &lt;code&gt;.git/hooks&lt;/code&gt; directory. Typically in a newly created repo we will want the initial commit to be on main, so the hook also checks there are any existing commits before blocking the commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;☁  brew@kelso:asda ➜ &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ git add README.md
☁  brew@kelso:asda ➜ &lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt; ✗ git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"adding initial documentation"&lt;/span&gt;
main branch commit is blocked
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>git</category>
    </item>
    <item>
      <title>Finding most recently updated projects with the 'recent' function</title>
      <dc:creator>cerico</dc:creator>
      <pubDate>Wed, 31 May 2023 06:55:23 +0000</pubDate>
      <link>https://dev.to/cerico/finding-most-recently-updated-projects-with-the-recent-function-2496</link>
      <guid>https://dev.to/cerico/finding-most-recently-updated-projects-with-the-recent-function-2496</guid>
      <description>&lt;h2&gt;
  
  
  Finding most recently updated projects with the 'recent' function
&lt;/h2&gt;

&lt;p&gt;The purpose of this &lt;code&gt;recent&lt;/code&gt; function is to find and print the most recently updated directories containing a specific file or folder, relative to the current directory. Lets say for example we wanted to find the 6 most recently updated directories containing a &lt;code&gt;Makefile&lt;/code&gt;, showing the date of the last updated file in each directory (ie not the Makefile itself)&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ recent 6 Makefile
Finding 6 most recent directories containing Makefile
&lt;span class="nt"&gt;---&lt;/span&gt;
2023-05-28 venlo
2023-05-08 observatory
2023-05-08 lighthouse-ii
2023-05-08 docker/getting-started/vaxjo
2023-05-08 contabo
2023-05-07 research/seacroft

19 total
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets look at the code&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;recent&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;recent &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c"&gt;# Find n most recent directories containing named file # ➜ recent 12 astro.config.mjs&lt;/span&gt;
  &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;1-9]&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;num&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;num&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10
  &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;.[:alpha:]]&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'.git'&lt;/span&gt;
  &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;1-9]&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;num&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
  &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;.[:alpha:]]&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;tmpfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;echo &lt;/span&gt;Finding &lt;span class="si"&gt;$(&lt;/span&gt;ColorCyan &lt;span class="nv"&gt;$num&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; most recent directories containing &lt;span class="si"&gt;$(&lt;/span&gt;ColorGreen &lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;---&lt;/span&gt;
  find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-maxdepth&lt;/span&gt; 5  &lt;span class="nt"&gt;-not&lt;/span&gt; &lt;span class="nt"&gt;-path&lt;/span&gt; &lt;span class="s1"&gt;'*node_modules*'&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="nv"&gt;$f&lt;/span&gt; &lt;span class="nt"&gt;-print&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; line&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;mod_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;_most_recent_in &lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;formatted_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;_format_dir_path &lt;span class="nv"&gt;$line&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$mod_date&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$formatted_dir&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tmpfile&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;done
  &lt;/span&gt;&lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tmpfile&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$num&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ColorCyan &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &amp;lt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tmpfile&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;))&lt;/span&gt;&lt;span class="s2"&gt; total"&lt;/span&gt;
  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tmpfile&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets break down whats happening here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;[[ $1 = [1-9]* ]] &amp;amp;&amp;amp; num=$1 || num=10&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the first argument is a number, then set the variable &lt;strong&gt;num&lt;/strong&gt; to this value. Otherwise, sets &lt;code&gt;num&lt;/code&gt; to the default value of 10.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;[[ $1 = [.[:alpha:]]* ]] &amp;amp;&amp;amp; f=$1 || f='.git'&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But if the first argument is a string then set the variable &lt;code&gt;f&lt;/code&gt; to this value. Otherwise, sets &lt;code&gt;f&lt;/code&gt; to the default value of '.git'.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;[[ $2 = [1-9]* ]] &amp;amp;&amp;amp; num=$2&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the second argument is a number, then set the variable &lt;code&gt;num&lt;/code&gt; to this value. Otherwise, &lt;code&gt;num&lt;/code&gt; remains its previous value, set on the first line.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;[[ $2 = [.[:alpha:]]* ]] &amp;amp;&amp;amp; f=$2&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And if the second argument is a string then set the variable &lt;code&gt;f&lt;/code&gt; to this value. Otherwise, &lt;code&gt;f&lt;/code&gt; remains its previous value, set on the second line.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;local tmpfile=$(mktemp)&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here a tmp file &lt;code&gt;tmpFile&lt;/code&gt; is creatd, to store the results of the search.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;find . -maxdepth 5 -not -path '_node_modules_' -name $f -print 2&amp;gt;/dev/null | while read -r line; do&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here we execute a find command, searching for the file or directory specified by the variable &lt;code&gt;f&lt;/code&gt;. We're limiting the search to a maximum depth of 5 directories, and excluding any directories named &lt;code&gt;node_modules&lt;/code&gt;. We're then piping the results of the find command to a while loop.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;local mod_date=$(_most_recent_in $dir)&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inside the while loop, we're calling the function &lt;code&gt;_most_recent_in&lt;/code&gt;, passing in the &lt;code&gt;line&lt;/code&gt; from the loop. The function &lt;code&gt;_most_recent_in&lt;/code&gt; returns the date of the most recently updated file or directory. We'll cover how this works in the next section.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;local formatted_dir=$(_format_dir_path $dir)&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Similarly, also inside the while loop, we're using the &lt;code&gt;_format_dir_path&lt;/code&gt; function to format the &lt;code&gt;line&lt;/code&gt; from the loop. We'll also cover this in the next section.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;echo "$mod_date $formatted_dir" &amp;gt;&amp;gt; "$tmpfile"&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, inside the while loop, we're printing the date and directory path of each found &lt;code&gt;f&lt;/code&gt; to the tmp file &lt;code&gt;tmpFile&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;eort -r "$tmpfile" | head -n $num&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And now back outside the loop, the temp file contains a list of all the found &lt;code&gt;f files and/or directories, which are then sorted in reverse order, and limited to a count of&lt;/code&gt;num` specified at the start of the function&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;_most_recent_in&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
_most_recent_in () {&lt;br&gt;
    [[ ! -n $1 ]] &amp;amp;&amp;amp; return&lt;br&gt;
    [[ -f $1 ]] &amp;amp;&amp;amp; term=$(dirname "$1") || term=$1/..&lt;br&gt;
    if [ $(uname) = 'Darwin' ]&lt;br&gt;
    then&lt;br&gt;
        find $term -type f -exec stat -f "%Sm" -t "%Y-%m-%d" {} + | sort -r | head -n 1&lt;br&gt;
    else&lt;br&gt;
        find $term -type f -exec stat --format="%y" {} + | sort -r | head -n 1 | cut -d' ' -f1&lt;br&gt;
    fi&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here we take in the line from the &lt;code&gt;recent&lt;/code&gt; function outlined in the preceding section, and return the date of the most recently updated file, relative to the passed in line. Lets look in more detail&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;[[ -f $1 ]] &amp;amp;&amp;amp; term=$(dirname "$1")  || term=$1/..&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the passed in line is a file, we set the variable &lt;code&gt;term&lt;/code&gt; to the directory of the file. Otherwise, we set &lt;code&gt;term&lt;/code&gt; to the parent directory of the passed in line. So if the passed in line is &lt;code&gt;reponame/README.md&lt;/code&gt; it will search for the most recently updated file in &lt;code&gt;reponame&lt;/code&gt;, and if the passed inline is &lt;code&gt;reponame/styles&lt;/code&gt;, where styles is a directory, it will search in &lt;code&gt;reponame/styles/..&lt;/code&gt;, which is &lt;code&gt;reponame&lt;/code&gt;. This ensure consistency, ie in both cases it searches the directory conraing what could be either a file or a subdirectory, not within the subdirectory itself.&lt;/p&gt;

&lt;p&gt;The rest of the function finds the stat of each file in the directory, sorts them in reverse order, and returns the first result. We're returning just the date here as we don't need the file or directory name. THe find syntax being slightly different for Mac and Linux, we check the uname first before running the approprate find command&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;_format_dir_path&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
_format_dir_path () {&lt;br&gt;
    echo $1 | awk '{sub(/\/[^\/]*$/, ""); print}' | awk -F'\\./' '{if ($2 == "") print "."; else print $2}'&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This admittedly dense oneliner, strips the &lt;code&gt;./&lt;/code&gt; from the beginning of the passed in path, and removes the final slash and everyhing after it. In the case of the file being in the current directory, eg &lt;code&gt;./Makefile&lt;/code&gt;, there is nothing to display, so it substitutes a &lt;code&gt;.&lt;/code&gt;. This last function only exists for tidier return values, ie &lt;code&gt;reponame&lt;/code&gt; and not &lt;code&gt;./reponame/styles&lt;/code&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  &lt;code&gt;ColorCyan&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;`&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ColorCyan &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; &lt;span class="s1"&gt;'\e[36m'&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s1"&gt;'\e[0m'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A small helper function used by &lt;code&gt;recent&lt;/code&gt; to color code output&lt;/p&gt;

&lt;h2&gt;
  
  
  Extending the function
&lt;/h2&gt;

&lt;p&gt;We can now extend the function to find commonly searched for directories. Lets say we commonly search for the most recently updated astro applications. We can make an &lt;code&gt;astros&lt;/code&gt; function that calls the &lt;code&gt;recent&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;astros&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;astros &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; recent &lt;span class="nv"&gt;$1&lt;/span&gt; astro.config.mjs &lt;span class="o"&gt;||&lt;/span&gt; recent 10 astro.config.mjs
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ astros 4
Finding 4 most recent directories containing astro.config.mjs
&lt;span class="nt"&gt;---&lt;/span&gt;
2023-05-28 venlo/template/astro
2023-05-28 dev.io37.ch
2023-05-07 seacroft
2023-04-15 created-by-venlo/bus-station

22 total
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





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

&lt;/div&gt;

</description>
      <category>shell</category>
    </item>
    <item>
      <title>Using brew in a multi-user system</title>
      <dc:creator>cerico</dc:creator>
      <pubDate>Tue, 30 May 2023 07:44:34 +0000</pubDate>
      <link>https://dev.to/cerico/using-brew-in-a-multi-user-system-2lnl</link>
      <guid>https://dev.to/cerico/using-brew-in-a-multi-user-system-2lnl</guid>
      <description>&lt;h2&gt;
  
  
  Using brew in a multi-user system
&lt;/h2&gt;

&lt;p&gt;On a mac brew can get into a bit of a muddle on a multi-user system if you are not careful.  The problem is that brew installs everything in &lt;code&gt;/usr/local&lt;/code&gt; and if you have multiple users then the permissions can get a bit messed up.  The answer to this is to install brew as normal for the first user, but any subsequent users shouldn't install their own version, but run the first users installation instead.&lt;/p&gt;

&lt;p&gt;To do this, set up an alias in your &lt;strong&gt;~/.zshrc&lt;/strong&gt; to run brew as that user.&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;# ~.zshrc&lt;/span&gt;
&lt;span class="nb"&gt;unalias &lt;/span&gt;brew 2&amp;gt;/dev/null
&lt;span class="nv"&gt;brewser&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"%Su"&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;which brew&lt;span class="si"&gt;))&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;brew&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'sudo -Hu '&lt;/span&gt;&lt;span class="nv"&gt;$brewser&lt;/span&gt;&lt;span class="s1"&gt;' brew'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets break this down.  The first line removes any existing alias for brew. This is because we need to 'real' brew in the second line to find the installation location (&lt;strong&gt;which brew&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;The second line gets the user that brew is installed under.  The third line creates an alias for brew that runs brew as the user that brew is installed under.  The &lt;code&gt;2&amp;gt;/dev/null&lt;/code&gt; just stops an error message if there is no existing alias (which we would get on the first sourcing of the file as in that instance brew would be the 'real' brew).&lt;/p&gt;

</description>
      <category>shell</category>
    </item>
    <item>
      <title>Upsearching directory tree</title>
      <dc:creator>cerico</dc:creator>
      <pubDate>Sun, 28 May 2023 10:14:24 +0000</pubDate>
      <link>https://dev.to/cerico/upsearching-directory-tree-2590</link>
      <guid>https://dev.to/cerico/upsearching-directory-tree-2590</guid>
      <description>&lt;h2&gt;
  
  
  Upsearching directory tree
&lt;/h2&gt;

&lt;p&gt;Sometimes I want to be able to find a particular file in a directory tree. I wrote a shell function called upsearch that is used to search for a file or directory from the current working directory upwards to root ("/") in the directory tree. This function can be handy in scenarios where you are trying to find a file or directory that exists at some level up in your directory structure, but you're not sure exactly where.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;upsearch &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;slashes&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PWD&lt;/span&gt;&lt;span class="p"&gt;//[^\/]/&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
  &lt;span class="nv"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;slashes&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; n&amp;gt;0&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nt"&gt;--n&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$directory&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$directory&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;return
    &lt;/span&gt;&lt;span class="nv"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$directory&lt;/span&gt;&lt;span class="s2"&gt;/.."&lt;/span&gt;
  &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's how the function works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;slashes=${PWD //[^\/]/}&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This line is counting the number of slash (&lt;strong&gt;&lt;code&gt;/&lt;/code&gt;&lt;/strong&gt;) characters in the current working directory path (&lt;strong&gt;&lt;code&gt;$PWD&lt;/code&gt;&lt;/strong&gt;) by removing all non-slash characters. The result is stored in the slashes variable. This is used to determine the maximum depth to search upwards in the directory tree.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;directory="$PWD"&lt;/code&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This line sets the directory variable to the current working directory.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;test -e "$directory/$1"`&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inside the for loop, this checks if the file or directory specified as the argument to the upsearch function (&lt;strong&gt;&lt;code&gt;$1&lt;/code&gt;&lt;/strong&gt;) exists in the current directory.&lt;/p&gt;

&lt;p&gt;If the file or directory exists (&lt;strong&gt;&lt;code&gt;test -e returns true&lt;/code&gt;&lt;/strong&gt;), it prints the directory path and then exits the function (&lt;strong&gt;&lt;code&gt;return&lt;/code&gt;&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;If the file or directory does not exist, the directory variable is updated to point one directory higher (&lt;strong&gt;&lt;code&gt;$directory/..&lt;/code&gt;&lt;/strong&gt;). This moves the search up one level in the directory structure.&lt;/p&gt;

&lt;p&gt;The loop repeats until it finds the file or directory, or it has searched up to the root directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
➜ upsearch .git&lt;br&gt;
➜ upsearch Makefile&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This can now be used by other functions:&lt;/p&gt;

&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;h4&gt;
  
  
  cdrepo
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
cdrepo () {&lt;br&gt;
  cd $(upsearch .git)&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If we're located somewhere inside a git repo then we can quickly jump to the root of the repo&lt;/p&gt;

&lt;h4&gt;
  
  
  m
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
m () {&lt;br&gt;
  mf=$(upsearch Makefile)&lt;br&gt;
  if [[ ${#mf} -gt 0 ]]; then&lt;br&gt;
    cd $mf&lt;br&gt;
    make $1&lt;br&gt;
  else&lt;br&gt;
    echo No Makefile found. Nothing to do&lt;br&gt;
  fi&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here we can run make from anywhere, and it will upsearch for the nearest Makefile and run it with the argument passed to the function. If no Makefile is found while traversing up the directory tree, it will print a message and do nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
➜ m help&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

</description>
      <category>shell</category>
    </item>
    <item>
      <title>Order-agnostic symbolic links</title>
      <dc:creator>cerico</dc:creator>
      <pubDate>Sun, 28 May 2023 01:21:55 +0000</pubDate>
      <link>https://dev.to/cerico/order-agnostic-symbolic-links-3985</link>
      <guid>https://dev.to/cerico/order-agnostic-symbolic-links-3985</guid>
      <description>&lt;h2&gt;
  
  
  Order-agnostic symbolic links
&lt;/h2&gt;

&lt;p&gt;I don't use symbolic links a whole bunch, but when I do use them I always forget the order of arguments in the &lt;code&gt;ln&lt;/code&gt; command. So I wrote a function that allows you to create a symbolic link in any order so I never have to think about it again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;isym &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="c"&gt;# Make symbolic link in any order # ➜ isym cats dogs&lt;/span&gt;
  &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="nv"&gt;$2&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nv"&gt;$2&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a breakdown of what happens within this function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;[[-e $1]]&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Initially, the function checks whether $1, the first parameter, is a valid file or directory (-e). This safety check prevents creating symbolic links to non-existent files or directories.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&amp;amp;&amp;amp; ln -s $1 $2&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If $1 is indeed a file or directory, the function creates a symbolic link to $1 named $2.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;|| ln -s $2 $1&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the first condition fails (i.e., $1 doesn't exist), the function attempts to create a symbolic link to $2, named $1.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;➜ isym &amp;lt;file&amp;gt; &amp;lt;&lt;span class="nb"&gt;link&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
➜ isym &amp;lt;&lt;span class="nb"&gt;link&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &amp;lt;file&amp;gt;
➜ isym /etc/asda.conf ~/asda.conf
➜ isym ~/asda.conf /etc/asda.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now this can be used in any order without worrying about the order of the arguments.&lt;/p&gt;

</description>
      <category>shell</category>
    </item>
  </channel>
</rss>
