<?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: Juan Julián Merelo Guervós</title>
    <description>The latest articles on DEV Community by Juan Julián Merelo Guervós (@jj).</description>
    <link>https://dev.to/jj</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%2F8410%2F500.jpeg</url>
      <title>DEV Community: Juan Julián Merelo Guervós</title>
      <link>https://dev.to/jj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jj"/>
    <language>en</language>
    <item>
      <title>You can also have (some) type safety in JavaScript. Here's how.</title>
      <dc:creator>Juan Julián Merelo Guervós</dc:creator>
      <pubDate>Mon, 10 Feb 2025 09:42:12 +0000</pubDate>
      <link>https://dev.to/jj/you-can-also-have-some-type-safety-in-javascript-heres-how-325a</link>
      <guid>https://dev.to/jj/you-can-also-have-some-type-safety-in-javascript-heres-how-325a</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/jj" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F8410%2F500.jpeg" alt="jj"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/jj/destructuring-for-type-safety-in-javascript-17l4" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Destructuring for type safety in JavaScript&lt;/h2&gt;
      &lt;h3&gt;Juan Julián Merelo Guervós ・ Jul 25 '23&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#typesafety&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#programmingpatterns&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>javascript</category>
      <category>typesafety</category>
      <category>programmingpatterns</category>
    </item>
    <item>
      <title>Writing extensions to git opens up lots of possibilities of enhancing teamwork through a commonly used tool. And #perl will make it easier.</title>
      <dc:creator>Juan Julián Merelo Guervós</dc:creator>
      <pubDate>Mon, 10 Feb 2025 09:35:53 +0000</pubDate>
      <link>https://dev.to/jj/writing-extensions-to-git-opens-up-lots-of-possibilities-of-enhancing-teamwork-through-a-commonly-3b35</link>
      <guid>https://dev.to/jj/writing-extensions-to-git-opens-up-lots-of-possibilities-of-enhancing-teamwork-through-a-commonly-3b35</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/jj" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F8410%2F500.jpeg" alt="jj"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/jj/writing-git-extensions-in-perl-3ken" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Writing git extensions in Perl&lt;/h2&gt;
      &lt;h3&gt;Juan Julián Merelo Guervós ・ Feb 10&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#git&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#workflows&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#teamwork&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#perl&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>git</category>
      <category>workflows</category>
      <category>teamwork</category>
      <category>perl</category>
    </item>
    <item>
      <title>Writing git extensions in Perl</title>
      <dc:creator>Juan Julián Merelo Guervós</dc:creator>
      <pubDate>Mon, 10 Feb 2025 09:34:43 +0000</pubDate>
      <link>https://dev.to/jj/writing-git-extensions-in-perl-3ken</link>
      <guid>https://dev.to/jj/writing-git-extensions-in-perl-3ken</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Most people will tell you git is a source control tool; some people will tell you that git is a &lt;a href="https://git-scm.com/book/en/v2/Git-Internals-Git-Objects" rel="noopener noreferrer"&gt;content-addressable filesystem&lt;/a&gt;. It's all that, but the interesting thing is that it's a single-tool interface to frameworks that allow you to create products as a team.&lt;br&gt;
Enter the absolutely simple extension mechanism that git has: write a n executable called git-xxx and &lt;code&gt;git&lt;/code&gt; will dutifully call it when you make git xxx. Which is why, to make an easier onramp for students in my 7th-semester class in Computer Science, I created an extension called &lt;code&gt;git iv&lt;/code&gt; (IV is the acronym for the class). The extension allows them to create branches with specific names, as well as upload those branches, without needing to remember specific &lt;code&gt;git&lt;/code&gt; commands.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You might argue that remembering &lt;code&gt;git&lt;/code&gt; commands is what students should do, but in fact they don't, and since this is not part of the core of the class, I prefer to eliminate sources of trouble for them (which eventually become sources of trouble for me) using this.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Writing the extension in Perl
&lt;/h2&gt;

&lt;p&gt;There are many good things that can be said about Perl, for this or for anything else. But in this case there's a thing that makes it ideal for writing extensions: &lt;code&gt;git&lt;/code&gt; includes a Perl module called &lt;code&gt;Git&lt;/code&gt;, which is a Perl interface to all the Git commands. This is distributed with &lt;code&gt;git&lt;/code&gt;, so if you've got &lt;code&gt;git&lt;/code&gt;, you've got this library. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The whole extension is not hosted &lt;a href="https://github.com/JJ/git-iv" rel="noopener noreferrer"&gt;in this GitHub repo&lt;/a&gt;; this will contain the most up-to-date version as well as documentation and other stuff. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So here's the preamble to the extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;lib&lt;/span&gt; &lt;span class="sx"&gt;qw( /Library/Developer/CommandLineTools/usr/share/git-core/perl
            /usr/share/perl5 )&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;Git&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;v5&lt;/span&gt;&lt;span class="mf"&gt;.14&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$HELP_FLAG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-h&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$USAGE_STRING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;&amp;lt;EOC;
Uso:
    git iv objetivo &amp;lt;número&amp;gt; -- crea una rama para ese objetivo
    git iv sube-objetivo     -- sube al repo remoto la rama

    git iv $HELP_FLAG -- imprime este mensaje
EOC
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main caveat about the extension is that some flags will be handled by &lt;code&gt;git&lt;/code&gt; itself. There are probably quite a few of those, but one of them is &lt;code&gt;--help&lt;/code&gt;. &lt;code&gt;git xxx --help&lt;/code&gt; will try to look up a manual page for &lt;code&gt;git xxx&lt;/code&gt;. This is why above a different help flag is defined. And also a usage string, which is helpful when you don't remember the exact shape of the subcommands. In this case, I use &lt;code&gt;git iv&lt;/code&gt; as the extension name and as interface to the stuff that needs to be made; but there are subcommands that will do different things. These are implemented later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@subcommands&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;qw(objetivo sube-objetivo)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;@subcommands&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;quotemeta&lt;/span&gt; &lt;span class="nv"&gt;$HELP_FLAG&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nb"&gt;die&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;usage_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="nv"&gt;@ARGV&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$subcommand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nb"&gt;die&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No se reconoce el subcomando &lt;/span&gt;&lt;span class="si"&gt;$subcommand&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="sr"&gt;/\Q$subcommand/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;@subcommands&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;@ARGV&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I prefer not to include any dependencies; there are powerful command line flag libraries out there, but in this case, a single script is best. So you handle whatever comes after &lt;code&gt;iv&lt;/code&gt; uniformly, be it a subcommand or a flag. But the issue with the flag is that it includes a dash &lt;code&gt;-&lt;/code&gt;, so we wrap it so that it can be used safely in regexes. Like the one, for instance, 4 lines below: in case the subcommand at the front of the command line is not part of the list, it will bail out showing the usage string.&lt;/p&gt;

&lt;p&gt;Anything after the subcommand will be gobbled into &lt;code&gt;@args&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$subcommand&lt;/span&gt; &lt;span class="ow"&gt;eq&lt;/span&gt; &lt;span class="nv"&gt;$HELP_FLAG&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;say&lt;/span&gt; &lt;span class="nv"&gt;$USAGE_STRING&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$repo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;Git&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;die&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Aparentemente, no estás en un repositorio&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$subcommand&lt;/span&gt; &lt;span class="ow"&gt;eq&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;objetivo&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;die&lt;/span&gt; &lt;span class="nv"&gt;$USAGE_STRING&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="nv"&gt;@args&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$repo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkout&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-b&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Objetivo-&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$subcommand&lt;/span&gt; &lt;span class="ow"&gt;eq&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sube-objetivo&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$repo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;rev-parse&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--abbrev-ref&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HEAD&lt;/span&gt;&lt;span class="p"&gt;"&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;chomp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$branch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$repo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;command&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;push&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-u&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; &lt;span class="nv"&gt;$branch&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it's a matter of processing the subcommand. If it's the flag &lt;code&gt;-h&lt;/code&gt;, print the usage string; if it's any of the other subcommands, we need to work with the git repository.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$repo = Git-&amp;gt;repository;&lt;/code&gt; creates an object out of the &lt;code&gt;Git&lt;/code&gt; library we mentioned before that we will use to issue the different plumbing or high level commands. One of the subcommands will do a checkout: &lt;code&gt;$repo-&amp;gt;command( "checkout", "-b", "Objetivo-" . $args[0]);&lt;/code&gt; will convert itself to the equivalent command. You can even work with plumbing commands such as &lt;a href="https://git-scm.com/docs/git-rev-parse" rel="noopener noreferrer"&gt;&lt;code&gt;rev-parse&lt;/code&gt;&lt;/a&gt; to check the branch you're in and create that branch remotely, ad the other command does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concluding
&lt;/h2&gt;

&lt;p&gt;Perl saves you a whole lot of trouble when writing this kind of thing. Besides, the fact that it will be most probably be installed in any system you use to develop (Mac, Linux or WSL) will save you trouble asking for prerequisites for this script.&lt;/p&gt;

</description>
      <category>git</category>
      <category>workflows</category>
      <category>teamwork</category>
      <category>perl</category>
    </item>
    <item>
      <title>Creating new Perl composite actions from a repository template</title>
      <dc:creator>Juan Julián Merelo Guervós</dc:creator>
      <pubDate>Wed, 22 May 2024 12:06:18 +0000</pubDate>
      <link>https://dev.to/jj/creating-new-perl-composite-actions-from-a-repository-template-3c0k</link>
      <guid>https://dev.to/jj/creating-new-perl-composite-actions-from-a-repository-template-3c0k</guid>
      <description>&lt;p&gt;So you want to create a quick-and-dirty GitHub actions that does only one thing and does it well, or glues together several actions, or simply to show off a bit at your next work interview. Here's how you can do it.&lt;br&gt;
Let me introduce you to &lt;a href="https://docs.github.com/en/actions/creating-actions/creating-a-composite-action"&gt;composite GitHub actions&lt;/a&gt;, one of the three types that are there (the other are JavaScript GHAs or container-based GHAs) and maybe one of the most widely unknown. However, they have several things going for them. First, they have low latency: no need to download a container or to set up some JS environment). Second, they are relatively easy to set up: they can be self-contained, with everything needed running directly on the description of the GitHub action. Third, you can leverage &lt;a href="https://github.com/actions/runner-images"&gt;all the tools installed on the runner&lt;/a&gt; like bash, compilers, build tools... or Perl, which can be that and much more.&lt;br&gt;
Even being easy, it is even easier if you have a bit of boilerplate you can use directly or adapt to your own purposes. This is what has guided the creation of &lt;a href="https://github.com/JJ/perl-composite-action-template"&gt;the template for a composite GitHub action based on Perl&lt;/a&gt;. It is quite minimalistic. But let me walk you through what it has got so that you can use it easier&lt;/p&gt;

&lt;p&gt;First, this &lt;code&gt;action.yml&lt;/code&gt; describes what it does and how it does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Hello&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Perl'&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Perl&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Github&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Action&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Template'&lt;/span&gt;
&lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;template-input&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Change this&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;What&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;it&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;about'&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="c1"&gt;# or not&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;World'&lt;/span&gt;
&lt;span class="na"&gt;runs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;using&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;composite"&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;print %ENV;&lt;/span&gt;
      &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;perl {0}&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${GITHUB_ACTION_PATH}/action.pl&lt;/span&gt;
      &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will have to customize inputs as well as outputs here (and, of course, name and description), but the steps are already baked in. It even includes the correct path to the (downloaded) Github action: when you're acting on a repository, the place where a GHA is is contained in an environment variable, &lt;code&gt;GITHUB_ACTION_PATH&lt;/code&gt;. You can access it that way.&lt;/p&gt;

&lt;p&gt;In general, that script might need external libraries, even your own, which you might have moved out of the script for testing purposes (&lt;em&gt;everything&lt;/em&gt; must be tested). That is why the action also contains &lt;code&gt;App::FatPacker&lt;/code&gt; as a dependency; that's a bundler that will put the source (&lt;code&gt;action.src.pl&lt;/code&gt;), your library (&lt;code&gt;lib/Action.pm&lt;/code&gt;) and every other module you might have used into a single file, the &lt;code&gt;action.pl&lt;/code&gt; referenced above.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;Makefile&lt;/code&gt; is also provided, so that, after installing &lt;code&gt;fatpack&lt;/code&gt;, typing &lt;code&gt;make&lt;/code&gt; will process the source and generate the script.&lt;/p&gt;

&lt;p&gt;And that's essentially it. &lt;a href="https://github.com/new?template_name=perl-composite-action-template"&gt;Use the template&lt;/a&gt; and create your new (composite) action in just a few minutes! &lt;/p&gt;

</description>
      <category>perl</category>
      <category>githubactions</category>
      <category>repositorytemplates</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>How to generate a great website and reference manual for your R package</title>
      <dc:creator>Juan Julián Merelo Guervós</dc:creator>
      <pubDate>Wed, 10 Apr 2024 08:52:15 +0000</pubDate>
      <link>https://dev.to/jj/how-to-generate-a-great-website-and-reference-manual-for-your-r-package-33al</link>
      <guid>https://dev.to/jj/how-to-generate-a-great-website-and-reference-manual-for-your-r-package-33al</guid>
      <description>&lt;p&gt;Generating a website for your &lt;a href="https://r-project.org"&gt;R&lt;/a&gt; package is always a great idea. If the package is based on some paper, it will help it get noticed and eventually used. And once you have a website, it's just as well to include a reference manual for the package in it, that complements or is a bit more updated than the one published in CRAN. Or simply in another format.&lt;/p&gt;

&lt;p&gt;That's what I tried to do for my package &lt;a href="https://cran.r-project.org/package=dupNodes"&gt;dupNodes&lt;/a&gt;. I found &lt;a href="https://pkgdown.r-lib.org/"&gt;&lt;code&gt;pkgdown&lt;/code&gt;&lt;/a&gt;, an one-stop solution for generating websites, which even generates the GitHub action for you.&lt;/p&gt;

&lt;p&gt;I had a problem, though. I already had some semblance of &lt;a href="https://jj.github.io/r-dupNodes"&gt;website&lt;/a&gt;, using Jekyll to generate the pages from markdown, and just plain HTML elsewhere, for presentations and other stuff. And &lt;code&gt;pkgdown&lt;/code&gt; generated an &lt;code&gt;index.html&lt;/code&gt;, overwriting at least the main stuff, and not including some images I had in the &lt;code&gt;README.md&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;So I decided to combine both in the same workflow. Here's what I had initially&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy Jekyll with GitHub Pages dependencies preinstalled&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;master"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

  &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
  &lt;span class="na"&gt;pages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
  &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

&lt;span class="na"&gt;concurrency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pages"&lt;/span&gt;
  &lt;span class="na"&gt;cancel-in-progress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# Build job&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;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;Checkout&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="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;Setup Pages&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/configure-pages@v4&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;Build with Jekyll&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/jekyll-build-pages@v1&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;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./&lt;/span&gt;
          &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./_site&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the default GitHub action generated by GitHub itself, pretty vanilla stuff. What it says is that it generates the HTML stuff to be deployed in a subdirectory called &lt;code&gt;_site&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So far so good. The only thing you have to do is to tell &lt;code&gt;pkgdown&lt;/code&gt; to generate it stuff in the same directory (or subdirectory thereof), right? &lt;/p&gt;

&lt;p&gt;Wrong. &lt;code&gt;jekyll-build-pages&lt;/code&gt; uses a docker container for generating the image, and that one does not follow the best practice of using a non-privileged user. Everything generated in &lt;code&gt;_site&lt;/code&gt; has &lt;code&gt;root&lt;/code&gt; privileges, so &lt;code&gt;pkgdown&lt;/code&gt; failed with an &lt;code&gt;EACCESS&lt;/code&gt; error.&lt;/p&gt;

&lt;p&gt;So I had to add this step after that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Change permissions built site&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sudo chown -R runner _site&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple enough, but required some research to check who was the user running the GitHub action. After that, it was easy to incorporate the rest of the GitHub action, copypasta from the one generated by &lt;code&gt;pkgdown&lt;/code&gt; itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;r-lib/actions/setup-pandoc@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;r-lib/actions/setup-r@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;use-public-rspm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;r-lib/actions/setup-r-dependencies@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;extra-packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;any::pkgdown, local::.&lt;/span&gt;
          &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;website&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;Build site&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE, dest_dir="_site/docs")&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rscript {0}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only change over the original is the &lt;code&gt;dest_dir&lt;/code&gt; right above this: it's generated to &lt;code&gt;_site/docs&lt;/code&gt;, within the &lt;code&gt;_site&lt;/code&gt; directory that will be packaged for deployment.&lt;/p&gt;

&lt;p&gt;Just one thing left. &lt;code&gt;pkgdown&lt;/code&gt; does not seem to be aware of images linked from the &lt;code&gt;README.md&lt;/code&gt; when it generates the cover for the site. So these had to be added too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Link images&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cp -r _site/img _site/docs/&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;Upload artifact&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/upload-pages-artifact@v3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initial version used symbolic links, but &lt;code&gt;upload-pages-artifact&lt;/code&gt; choked on it, so I had to physically copy the directory.&lt;/p&gt;

&lt;p&gt;And &lt;a href="https://jj.github.io/R-dupNodes"&gt;that's it now&lt;/a&gt;. A website with reference and articles, updated automatically, without having to wait for CRAN updates!&lt;/p&gt;

</description>
      <category>r</category>
      <category>githubactions</category>
      <category>jekyll</category>
    </item>
    <item>
      <title>Destructuring for type safety in JavaScript</title>
      <dc:creator>Juan Julián Merelo Guervós</dc:creator>
      <pubDate>Tue, 25 Jul 2023 09:50:25 +0000</pubDate>
      <link>https://dev.to/jj/destructuring-for-type-safety-in-javascript-17l4</link>
      <guid>https://dev.to/jj/destructuring-for-type-safety-in-javascript-17l4</guid>
      <description>&lt;p&gt;JavaScript allows you to use classes for modularization, but it's not easy to make a function just work with a single type. Let's see an example, taken from &lt;a href="https://github.com/JJ/nodeo"&gt;NodEO&lt;/a&gt;, an &lt;a href="https://www.techtarget.com/whatis/definition/evolutionary-algorithm"&gt;evolutionary algorithm&lt;/a&gt; library, now in a heavy refactorization phase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;StringChromosome&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Chromosome&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fitness&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fitness&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringChr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aString&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// more stuff&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;FloatChromosome&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Chromosome&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;aVector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fitness&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fitness&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floatVector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aVector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// more stuff here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A chromosome has a common fitness, that is, how well it solves the problem, plus a data structure that represents the problem; EAs don't tell you which data structure you should use, so different types of chromosomes will have different data structures; we will use different names for those attributes, reflecting what they actually are.&lt;/p&gt;

&lt;p&gt;Chromosomes undergo &lt;em&gt;mutation&lt;/em&gt;; they essentially change randomly, looking for a better solution. A first version of a mutation operator would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// defined within FloatChromosome&lt;/span&gt;
&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;mutationRange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;tatic&lt;/span&gt; &lt;span class="nx"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chromosome&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;floatVector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chromosome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floatVector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mutation_point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;floatVector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;floatVector&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mutation_point&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mutation_point&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mutationRange&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
      &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mutationRange&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works as expected, returning a new data structure that can be used to build a new chromosome (chromosomes are immutable, so it will need something else to compute the fitness before we build one).&lt;/p&gt;

&lt;p&gt;However, chromosome might or might not be a &lt;code&gt;FloatVector&lt;/code&gt;, so we would need to add some type checks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chromosome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FloatChromosome&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;chromosome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is not a FloatChromosome`&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will throw if we try to mutate a chromosome that has not been built as a &lt;code&gt;FloatChromosome&lt;/code&gt;. So this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sChrom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;StringChromosome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0001&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FloatChromosome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sChrom&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will throw:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;file:///home/jmerelo/Code/js/dev.to-js-types/lib/chromosomes.js:30
      throw new Error(
            ^

Error: StringChromosome is not a FloatChromosome
    at FloatChromosome.mutate (file:///home/jmerelo/Code/js/dev.to-js-types/lib/chromosomes.js:30:13)
    at file:///home/jmerelo/Code/js/dev.to-js-types/script/mutate.js:7:29
    at ModuleJob.run (node:internal/modules/esm/module_job:194:25)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, these type checks are neither complete (what if it does not have a constructor?) nor precise (what if it does actually have a &lt;code&gt;floatVector&lt;/code&gt; attribute, even if it's not been built with that class?).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notReallyAChrom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;floatVector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FloatChromosome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;notReallyAChrom&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will still throw, although we would have been perfectly able to work with it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;file:///home/jmerelo/Code/js/dev.to-js-types/lib/chromosomes.js:30
      throw new Error(
            ^

Error: Object is not a FloatChromosome
    at FloatChromosome.mutate (file:///home/jmerelo/Code/js/dev.to-js-types/lib/chromosomes.js:30:13)
    at file:///home/jmerelo/Code/js/dev.to-js-types/script/mutate.js:7:29
    at ModuleJob.run (node:internal/modules/esm/module_job:194:25)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Destructuring FTW
&lt;/h2&gt;

&lt;p&gt;We can easily fix this using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment"&gt;destructuring for the function arguments&lt;/a&gt;. Let's define the function so that it extracts from the incoming data structure just what we need, and only what we need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;floatVector&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;floatVector&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Incorrect data structure: no floatVector attribute&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mutation_point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;floatVector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;temp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;floatVector&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mutation_point&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;mutation_point&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mutationRange&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
      &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mutationRange&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fChrom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FloatChromosome&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FloatChromosome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fChrom&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notReallyAChrom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;floatVector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FloatChromosome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;notReallyAChrom&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wrongChrom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;StringChromosome&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;010&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;FloatChromosome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrongChrom&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Will work correctly until it finds the last chromosome. In that case, it will produce an error. By using destructuring we find (kind of) type safety, since we will reject all invalid data structures at the same time we accept data structures we can work with, whether they are declared as a class or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coda
&lt;/h2&gt;

&lt;p&gt;If you &lt;em&gt;really&lt;/em&gt; want type safety, you should go for the real type-safe JavaScript, that is, &lt;a href="https://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt;. If you need to stay with JS for some reason (and there are many good reasons to do so), you can achieve a certain kind of cleanliness using this programming pattern.&lt;/p&gt;

&lt;p&gt;You can find the final version of the code used in this paper in &lt;a href="https://github.com/JJ/dev.to-js-types"&gt;this repo&lt;/a&gt;. Previous versions can be found in the commit history.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typesafety</category>
      <category>programmingpatterns</category>
    </item>
    <item>
      <title>Follow that ship! With AISstreamer</title>
      <dc:creator>Juan Julián Merelo Guervós</dc:creator>
      <pubDate>Wed, 10 May 2023 16:31:23 +0000</pubDate>
      <link>https://dev.to/jj/follow-that-ship-with-aisstreamer-21g3</link>
      <guid>https://dev.to/jj/follow-that-ship-with-aisstreamer-21g3</guid>
      <description>&lt;p&gt;Imagine you're doing OSINT to track the shenanigans of some ship, or simply want to know where your family, who is cruising on the Mediterranean, is more or less precisely once their phones lack signal in the (more or less high) seas... Well, the &lt;a href="https://en.wikipedia.org/wiki/Automatic_identification_system"&gt;Automatic data system AIS&lt;/a&gt; has you covered. It's mandatory that ships have automatic transmitter that broadcast data on where the ship is, as well as a certain amount of additional data, like destination.&lt;/p&gt;

&lt;p&gt;This data is available in several per-pay and free APIs. A good one, which is for the time being experimental, is &lt;a href="https://github.com/aisstream/example"&gt;AISstream&lt;/a&gt;. This is a streaming API: you connect to a websocket, that sends you event; it makes sense, since continuous polling would probably bring down any server. Still, you have to set up some stuff, look for the correct library, and so on... &lt;a href="https://www.npmjs.com/package/aisstreamer"&gt;AISstreamer&lt;/a&gt; (which tries, and fails, to create a portmanteau between steamer and streamer) has you covered.&lt;/p&gt;

&lt;p&gt;Install it via&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i aisstreamer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you're good to go with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { AIStrack } from "AISstreamer";
/** API_KEY needs to be defined as an environment variable */
const API_KEY = process.env.AISSTREAM_API_KEY;

/** The exact ship name needs to be used; this might include the company name */
const SHIP_NAME = process.env.SHIP_NAME.toUpperCase();

AIStrack(API_KEY, SHIP_NAME);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a single function in the API. It takes two mandatory arguments, the API key (which you will have assigned to the environment variable AISSTREAM_API_KEY) and the ship name (environment variable SHIP_NAME). AISStrack will print the whole message that includes this ship, via its default callback that simply does that, printing the variable; the other default is the bounding box where you want to look: by default it's the whole world, you can reduce it and pass it as the third argument.&lt;/p&gt;

&lt;p&gt;Since the default callback function prints the latest AIS message the ship has emitted, it will go more or less like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;Message:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;ShipStaticData:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;AisVersion:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;CallSign:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'ZGIJ&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;Destination:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'ANTIBES'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;Dimension:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;Dte:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;Eta:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;FixType:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;ImoNumber:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9794549&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;MaximumStaticDraught:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;3.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;MessageID:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;Name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'PAPA'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;RepeatIndicator:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;Spare:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;Type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;UserID:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;319156300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;Valid:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;MessageType:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'ShipStaticData'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;MetaData:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;MMSI:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;319156300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;ShipName:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'PAPA'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;latitude:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;43.587181666666666&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;longitude:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;7.130946666666667&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;time_utc:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="mi"&gt;2023-05-10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;46&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;52.509865357&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;0000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;UTC'&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generally you will find two types of position messages: &lt;code&gt;ShipStaticData&lt;/code&gt;, which, if I understand it correctly, is emitted when the ship is anchored, and &lt;code&gt;PositionReport&lt;/code&gt;. &lt;a href="https://aisstream.io"&gt;AISstream&lt;/a&gt; does a good job of picking up earth stations, but apparently not satellites, so you will not always be able to track the position, only when it's close to one of them. At any rate, the most interesting piece of information is in the &lt;code&gt;MetaData&lt;/code&gt; key, which is common to all messages: you have the latitude and longitude, which tell you where the ship is. From this static data you can also glean a couple of bits of data: &lt;code&gt;Destination&lt;/code&gt; as well as &lt;code&gt;Eta&lt;/code&gt; or estimated time of arrival (I guess, didn't check this much more).&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://aisstream.io/documentation#PositionReport"&gt;&lt;code&gt;PositionReport&lt;/code&gt;&lt;/a&gt; do have a &lt;code&gt;TrueHeading&lt;/code&gt; and &lt;code&gt;NavigationalStatus&lt;/code&gt;, which you can also use to predict future position, I guess (I haven't really tried).&lt;/p&gt;

&lt;p&gt;The possibilities are endless, from the simple, which is tracking and saving a ship's position, to the more advanced, like tracking a whole fleet.&lt;/p&gt;

&lt;p&gt;And this is just the first version; future versions might include multi-ship tracking, automatic messages based on type of message, and a Lot of Cool Stuff (as well as your suggestions &lt;a href="https://github.com/JJ/AISstreamer"&gt;here&lt;/a&gt; or anywhere.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Testing your Raku module using Github Actions</title>
      <dc:creator>Juan Julián Merelo Guervós</dc:creator>
      <pubDate>Fri, 14 Jan 2022 18:26:18 +0000</pubDate>
      <link>https://dev.to/jj/testing-your-raku-module-using-github-actions-109k</link>
      <guid>https://dev.to/jj/testing-your-raku-module-using-github-actions-109k</guid>
      <description>&lt;p&gt;Using &lt;a href="https://github.com/JJ/raku-test-action"&gt;the Raku test GitHub Action&lt;/a&gt; (&lt;a href="https://github.com/marketplace/actions/raku-test-action"&gt;here&lt;/a&gt; in the marketplace), copy this into a &lt;code&gt;test-raku.yaml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pull_request&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;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;Checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test via install&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;JJ/raku-test-action@main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That will install (and cache) dependencies before testing, and then do the test.&lt;/p&gt;

&lt;p&gt;That's it ⃰.&lt;/p&gt;




&lt;p&gt;⃰ I guess a bit of a making of is in order.&lt;/p&gt;

&lt;p&gt;This is what is called a &lt;a href="https://docs.github.com/es/actions/creating-actions/creating-a-composite-action?learn=create_actions&amp;amp;learnProduct=actions"&gt;composite GitHub action&lt;/a&gt;. These types of actions enable the possibility of doing several steps, and we needed that, mainly to take care of the cache. How to do so was the subject of &lt;a href="https://dev.to/jj/designing-containers-for-github-actions-5h84"&gt;my previous post&lt;/a&gt;. While the cache is going to be generated &lt;em&gt;outside&lt;/em&gt; the container, the rest of the action is going to run &lt;em&gt;inside&lt;/em&gt; a container. Except for setting up the cache, the rest is run inside &lt;a href="https://github.com/jj/alpine-raku/pkgs/container/raku-zef-gha"&gt;a container that runs this image&lt;/a&gt; and that has been designed specifically for GitHub actions (using what's mentioned above).&lt;/p&gt;

&lt;p&gt;What happens if this one-size-fits-all does not fit you? Well, you can still use &lt;a href="https://github.com/jj/alpine-raku/pkgs/container/raku-zef-gha"&gt;&lt;code&gt;Github::Actions&lt;/code&gt;&lt;/a&gt;, that comes with its own container. But I guess that'll be the topic of a different post.&lt;/p&gt;

</description>
      <category>raku</category>
      <category>githubactions</category>
      <category>testing</category>
      <category>tdd</category>
    </item>
    <item>
      <title>Designing containers for GitHub actions</title>
      <dc:creator>Juan Julián Merelo Guervós</dc:creator>
      <pubDate>Thu, 30 Dec 2021 12:11:01 +0000</pubDate>
      <link>https://dev.to/jj/designing-containers-for-github-actions-5h84</link>
      <guid>https://dev.to/jj/designing-containers-for-github-actions-5h84</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/features/actions"&gt;GitHub actions&lt;/a&gt; are workflows triggered by GitHub events. They are quite flexible, they run by default in "bare" operating system runners, but, additionally, they can be &lt;a href="https://docs.github.com/es/actions/using-github-hosted-runners/about-github-hosted-runners#docker-container-filesystem"&gt;hosted in user-provided containers&lt;/a&gt;. The problem is, GitHub expect these containers to have certain services and structure, which is a problem if you use a standard or plain container.&lt;/p&gt;

&lt;p&gt;Through the creation of &lt;a href="https://github.com/JJ/alpine-raku/"&gt;this container&lt;/a&gt;, and multiple questions in the &lt;a href="https://github.community"&gt;GitHub community&lt;/a&gt;, I've kinda discovered what are the things needed for containers to be used seamlessly in workflows. Here're the tips:&lt;/p&gt;

&lt;h2&gt;
  
  
  GHA user is UID=1001
&lt;/h2&gt;

&lt;p&gt;While the first user you will create in any Linux system will have UID equal to 1000, GHAs are a bit special and they use UID 1001. So you'd better use that UID for your user, as is done in this &lt;a href="https://github.com/JJ/docker-raku"&gt;Raku container&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;alpine:latest&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;

&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; RAKU_RELEASE=2021.12&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PKGS="git make gcc musl-dev perl linux-headers bash"&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk upgrade &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nv"&gt;$PKGS&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git clone &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 &lt;span class="nt"&gt;--branch&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RAKU_RELEASE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; https://github.com/MoarVM/MoarVM.git &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;MoarVM &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; perl Configure.pl &lt;span class="nt"&gt;--prefix&lt;/span&gt; /usr &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make &lt;span class="nt"&gt;--print-data-base&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make &lt;span class="nb"&gt;install&lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; .. &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git clone &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 &lt;span class="nt"&gt;--branch&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RAKU_RELEASE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; git://github.com/Raku/nqp.git &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;nqp &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; perl Configure.pl &lt;span class="nt"&gt;--backends&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;moar &lt;span class="nt"&gt;--prefix&lt;/span&gt; /usr &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; .. &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git clone &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 &lt;span class="nt"&gt;--branch&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RAKU_RELEASE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; https://github.com/rakudo/rakudo.git &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;rakudo &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; perl Configure.pl &lt;span class="nt"&gt;--backends&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;moar &lt;span class="nt"&gt;--prefix&lt;/span&gt; /usr &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;ls&lt;/span&gt; /usr/share/nqp/

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:latest&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; UID=1000&lt;/span&gt;

&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; version="0.5.0" maintainer="JJMerelo@GMail.com" raku_release=${RAKU_RELEASE} raku_user_uid=${UID}&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=base /usr/lib/libmoar.so /usr/lib&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=base /usr/share/nqp/ /usr/share/nqp&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=base /usr/share/perl6/ /usr/share/perl6&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=base /usr/bin/moar /usr/bin/nqp /usr/bin/raku /usr/bin/perl6 /usr/bin/rakudo /usr/bin/&lt;/span&gt;


&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /github &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; addgroup &lt;span class="nt"&gt;-S&lt;/span&gt; raku  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; adduser &lt;span class="nt"&gt;-S&lt;/span&gt; raku &lt;span class="nt"&gt;-G&lt;/span&gt; raku &lt;span class="nt"&gt;--uid&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;UID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; raku&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /home/raku&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["raku"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a bit longish, but the real deal is just above these lines. You can use that UID by default, but in my case I wanted two versions, just in case I needed more modifications later for GHS&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Spoiler: I did&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I use an &lt;code&gt;ARG&lt;/code&gt; that carries the UID, with 1000 by default, and there's a specific &lt;code&gt;build-arg&lt;/code&gt; for GHAs. And that's that.&lt;/p&gt;

&lt;p&gt;Is that all? Not really.&lt;/p&gt;

&lt;h2&gt;
  
  
  GHAs reset &lt;code&gt;HOME&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The container mounts a directory into &lt;code&gt;/github/home&lt;/code&gt; and then calls it &lt;code&gt;$HOME&lt;/code&gt;. Don't really &lt;a href="https://github.community/t/dont-quite-see-the-point-of-container-runners-setting-home/218476"&gt;know why&lt;/a&gt;, but it does. This can wreak a bit of havoc in languages for containers that expect home to be where it was originally used to install whatever. That can be easily fixed by just setting &lt;code&gt;HOME&lt;/code&gt; manually at a job level, but it can also be fixed adjusting the search path for installed modules, which in the case of Raku means &lt;a href="https://docs.raku.org/language/modules#Finding_installed_modules"&gt;setting &lt;code&gt;RAKULIB&lt;/code&gt;&lt;/a&gt;, as done &lt;a href="https://github.com/JJ/alpine-raku/blob/master/github-actions.Dockerfile"&gt;here&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ghcr.io/jj/raku-gha&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PKGS="git tar" PKGS_TMP="make gcc linux-headers musl-dev" WORKDIR="/home/raku"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; version="1.0.3" maintainer="JJMerelo@GMail.com" rakuversion=$VER&lt;/span&gt;

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; root&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk upgrade &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nv"&gt;$PKGS&lt;/span&gt; &lt;span class="nv"&gt;$PKGS_TMP&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; raku&lt;/span&gt;

&lt;span class="c"&gt;# Environment&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH="${WORKDIR}/.raku/bin:${WORKDIR}/.raku/share/perl6/site/bin:${PATH}" \&lt;/span&gt;
    ENV="${WORKDIR}/.profile"\
    RAKULIB="inst&lt;span class="c"&gt;#/home/raku/.raku"&lt;/span&gt;

&lt;span class="c"&gt;# Basic setup, programs and init&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; $WORKDIR&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;git clone &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 https://github.com/ugexe/zef.git &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;zef &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; raku &lt;span class="nt"&gt;-I&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; bin/zef &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; zef &lt;span class="nb"&gt;install &lt;/span&gt;Linenoise &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&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="nt"&gt;-rf&lt;/span&gt; zef

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; root&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk del &lt;span class="nv"&gt;$PGKS_TMP&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; raku&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["raku"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is already GHA-specific, as revealed by the base image. &lt;code&gt;$RAKULIB&lt;/code&gt; is defined midway through the file, with the &lt;code&gt;inst#&lt;/code&gt; prefix indicating it's already precompiled and ready to go. Raku will search there first, and then proceed to the supposed place, &lt;code&gt;inst#/github/home/.raku&lt;/code&gt;. Where there will be noting in the raw container. Will it &lt;em&gt;always&lt;/em&gt; be empty? Not by a long shot, because it will be used to install distributions by Raku, and thus it will be the place to cache. But something else is needed first.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check also that we're including &lt;code&gt;git&lt;/code&gt; in this image. Also useful for the checkout action (although this one has all kinds of defaults, and it's not actually essential).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  You need to install GNU tar first
&lt;/h2&gt;

&lt;p&gt;Alpine is an amazing little distro for containers, but part of its appeal comes from the fact that it puts a lot of external utilities into &lt;a href="https://busybox.net/"&gt;BusyBox&lt;/a&gt;, and that includes &lt;code&gt;tar&lt;/code&gt;. However, in an apparently undocumented move, &lt;a href="https://github.com/actions/cache"&gt;the caching GHA&lt;/a&gt; uses BSD or &lt;a href="https://www.gnu.org/software/tar/manual/tar.html"&gt;GNU tar&lt;/a&gt; for storing and restoring artifacts. That means it's needed in your container. Check above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PKGS="git tar" PKGS_TMP="make gcc linux-headers musl-dev" &lt;/span&gt;
// and later
&lt;span class="k"&gt;RUN &lt;/span&gt;apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk upgrade &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nv"&gt;$PKGS&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By installing &lt;code&gt;tar&lt;/code&gt;, the image will be a bit heavier (in this case, taking 10 seconds to download and start), but not really a big deal.&lt;/p&gt;

&lt;h2&gt;
  
  
  And you're ready to go
&lt;/h2&gt;

&lt;p&gt;See it in action &lt;a href="https://github.com/JJ/raku-dist-template/blob/master/.github/workflows/test.yaml"&gt;here&lt;/a&gt;. Once the container is GHA-ready, you can run (at least these) &lt;a href="https://github.com/actions"&gt;Github actions&lt;/a&gt; in your workflow as easily as if you were running the default base runner.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>githubactions</category>
      <category>ci</category>
      <category>cd</category>
    </item>
    <item>
      <title>How to totally over-engineer your CV, part two</title>
      <dc:creator>Juan Julián Merelo Guervós</dc:creator>
      <pubDate>Sat, 20 Mar 2021 09:02:04 +0000</pubDate>
      <link>https://dev.to/jj/how-to-totally-over-engineer-your-cv-part-two-1oip</link>
      <guid>https://dev.to/jj/how-to-totally-over-engineer-your-cv-part-two-1oip</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/jj/how-to-totally-over-engineer-your-cv-in-a-few-easy-steps-6ha"&gt;Part I of this series&lt;/a&gt; we focused on the text part and how to generate it automatically using GitHub actions.&lt;br&gt;
What would be a resume without a totally unnecessary programming timeline part, though? Something to show that you've been there, done that, gone through Pascal, you even used Windows in the 90s, and, well, a visual representation of your skills that gives an idea of how you can approach, and defeat, any JavaScript framework you're thrown at.&lt;br&gt;
So let's start with that timeline in a CSV file, something like &lt;a href="https://github.com/JJ/cv/blob/master/data/programming.csv" rel="noopener noreferrer"&gt;this&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Item, Group, Start Date, End Date
Basic, Languages, 1983-12-25, 1990-12-01
Pascal, Languages, 1988-09-01, 1993-01-01
C, Languages, 1989-01-01, 1993-12-01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... and so on. There's the item itself, the stuff you have experience with, then a group. I have &lt;code&gt;Languages&lt;/code&gt;, &lt;code&gt;Environments&lt;/code&gt; like Linux or cloud, and then &lt;code&gt;Tools&lt;/code&gt; like Docker; suit yourself here.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You might want "Sewing techniques" like "needlepoint" or "macramé" or "Zombie vanquishing" "with baseball bat" "sawed-off shotgun", whatever. Hey, any job, up to and including zombie exterminator, is well served with an over-engineered CV.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Start and end date are self-describing, only I am going to be using December this year, for purely aesthetic reasons, to indicate the stuff I'm still using now.&lt;/p&gt;

&lt;p&gt;I will render this using R; as usual, there are good R visualization libraries for mostly anything. I will use &lt;a href="https://github.com/shosaco/vistime" rel="noopener noreferrer"&gt;&lt;code&gt;vistime&lt;/code&gt;&lt;/a&gt;, an R module for &lt;code&gt;vis&lt;/code&gt;ualizing &lt;code&gt;time&lt;/code&gt;lines. But with a twist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight r"&gt;&lt;code&gt;&lt;span class="n"&gt;library&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"vistime"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;library&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"ggplot2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;read.csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"data/programming.csv"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gg_vistime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;col.event&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Item"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;col.start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Start.Date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;col.end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"End.Date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;col.group&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Group"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;axis.text.x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;element_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'blue4'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;coord_flip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;g.d&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ggplot_build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;g.d&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;90&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;rebuilt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ggplot_gtable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g.d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;png&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"img/timeline.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;960&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rebuilt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;dev.off&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;twist&lt;/em&gt; is that I wanted a vertical timeline, instead of an horizontal one; basically to fill one of the margins of one of the pages which was sitting empty, instead of occupying a big part of a page with a regular, horizontal, timeline. That's done in the second statement: a &lt;code&gt;ggplot2&lt;/code&gt; data structure is generated, which is basically a timeline with the &lt;code&gt;coord_flip&lt;/code&gt; statement that flips coordinates to make it vertical. However, the problem was that bar labels were not flipped. Besides, since some items started at pretty much the same time, they overlapped. The result was not nice, and had the right-level-of-engineering. So we had to overengineer it with the next 6 sentences.&lt;/p&gt;

&lt;p&gt;I knew that I had everything that's in the chart available in the &lt;code&gt;g&lt;/code&gt; data structure. Any other language, it would have been easy to dis-assemble that thing into its composed objects, and deal with the part that actually does the labeling. Not so easy with R. In order to have access to the data structure, you need to issue the &lt;code&gt;ggplot_build&lt;/code&gt; order.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Deconstructing through a command called &lt;code&gt;build&lt;/code&gt;, that's rich...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That creates a table with a series of data frames, and you can work with that. The fourth element &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;and yes, R starts its arrays with 1, just like Pascal. See? Learning Pascal was not so useless, after all (in fact, it was pretty useful and the best thing you could do in the 80s).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;contains a dataframe with all the bars. Flipping them is as easy as changing the angle to the right one, which happens to be the right angle too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight r"&gt;&lt;code&gt;&lt;span class="n"&gt;g.d&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;&lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="n"&gt;angle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;90&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, you still have a dis-assembled chart, which you need to assemble back, and then actually save to a file to have it ready.&lt;/p&gt;

&lt;p&gt;Running that is simply a matter of issuing a command from the command line. Creating a workflow that generates it is another matter. This might be &lt;a href="https://github.com/JJ/cv/blob/8678b636389a2e87f0a815bda82724aa15e7fd8f/.github/workflows/timeline.yaml" rel="noopener noreferrer"&gt;the first version you could think about&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Programming&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;timeline"&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;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data/*.csv'&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;creates-timeline&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;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;Checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Instala R&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;r-lib/actions/setup-r@v1&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;r-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.5.3'&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;Install packages&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sudo apt-get install libcurl4-openssl-dev&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;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;install.packages(c("ggplot2", "curl", "httr", "plotly", "vistime"))&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rscript {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;Ejecuta el script&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rscript .github/workflows/timeline.R&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Off the bat, pretty straightforward. Set up R using one of its version managers, install the downstream dependency packages you need, then install the actual packages that are going to be using (including some dependencies that should probably not be there but are anyway, to check that they are installed in the right order). And then run the script.&lt;/p&gt;

&lt;p&gt;That takes &lt;a href="https://github.com/JJ/cv/actions/runs/648755574" rel="noopener noreferrer"&gt;7 minutes&lt;/a&gt;. That's a lot for a timeline. Installing R packages includes compiling some C, and sometimes Fortran, source, plus all the rest. 7 minutes. We need to cut that down, if only to not include in the timeline a substantial part that says "Generating this timeline".&lt;br&gt;
Again, using a cache was discarded off the bat. Too much stuff in too many different places. But this time using a Docker image did make sense. That image will include pretty much the same, only it will come in a single, convenient, &lt;a href="https://github.com/JJ/cv/blob/master/Dockerfile" rel="noopener noreferrer"&gt;package&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; r-base&lt;/span&gt;

&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; version="0.0.1" maintainer="JJMerelo@GMail.com"&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libcurl4-openssl-dev r-cran-ggplot2 libssl-dev r-cran-httr git&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; R &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"install.packages(c( 'plotly', 'vistime'))"&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /home/docker&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["Rscript"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not a lot of overengineering here; just a straightforward installation of needed packages, and an entry point that enables us to use it as a R script runner.&lt;/p&gt;

&lt;p&gt;Did I say &lt;em&gt;not a lot of overengineering&lt;/em&gt;? Well, just the right amount. This is &lt;a href="https://hub.docker.com/r/jjmerelo/cv" rel="noopener noreferrer"&gt;public in Docker Hub&lt;/a&gt;, which will try and compile a new image &lt;em&gt;every single time&lt;/em&gt; you do a push. In most cases, there will be nothing new &lt;em&gt;in the Docker image&lt;/em&gt;, it will just be some data added to the CV. And yes, I could have a path-filtered Github Action that uploaded it either to Docker Hub or to GitHub registry? But why? It's quite simple to do just the compilations you need, using &lt;a href="https://dev.to/jj/writing-docker-hub-hooks-in-perl-44h2"&gt;Docker Hub hooks written in Perl&lt;/a&gt;, like &lt;a href="https://github.com/JJ/cv/tree/master/hooks" rel="noopener noreferrer"&gt;this one&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;Git&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;Git&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;Directory&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="p"&gt;');&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@modified_files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$repo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--name-only&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HEAD&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HEAD^&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
&lt;span class="nb"&gt;die&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;No Dockerfile modified in the last commit&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="p"&gt;")&lt;/span&gt;  &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="sr"&gt;/Dockerfile/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;@modified_files&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one is actually pretty standard, and can be used in any repo as long as your &lt;code&gt;Dockerfile&lt;/code&gt; is in the root dir. It checks if &lt;code&gt;Dockerfile&lt;/code&gt; is in the last commit, and dies if it does not, signalling the Docker Hub pipeline that it should not continue.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This appears as a failure in the pipeline; &lt;a href="https://github.com/docker/hub-feedback/issues/2064" rel="noopener noreferrer"&gt;there's no way to make the pipeline just stop instead of failing&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Again, this is possible because Perl is &lt;em&gt;also&lt;/em&gt; installed in the Docker Hub runner. It's everywhere.&lt;/p&gt;

&lt;p&gt;We can then use this Docker image-that's-only-regenerated-when-it-needs-to to in a renewed GitHub 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="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;Programming&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;timeline"&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;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;data/*.csv'&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;creates-timeline&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jjmerelo/cv&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;Checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ejecuta el script&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Rscript .github/workflows/timeline.R&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;Checks in results&lt;/span&gt;
        &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash {0}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;if [[ $(git status -s) ]]; then&lt;/span&gt;
              &lt;span class="s"&gt;git config --global user.email "jjmerelo@gmail.com"&lt;/span&gt;
              &lt;span class="s"&gt;git config --global user.name "CVDataBot"&lt;/span&gt;
              &lt;span class="s"&gt;git commit -am "Update timeline chart"&lt;/span&gt;
              &lt;span class="s"&gt;git push&lt;/span&gt;
          &lt;span class="s"&gt;else&lt;/span&gt;
              &lt;span class="s"&gt;echo "🟏 No Changes"&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The bulk of it is actually checking if there's been some change in the image (even if the data changes, maybe the image does not, for instance, if it's only a change in whitespace). But there's a small piece of overengineering there too, we'll get to that later.&lt;/p&gt;

&lt;p&gt;As shown in the &lt;code&gt;container&lt;/code&gt; key, this GitHub action is run &lt;em&gt;inside&lt;/em&gt; the container, so what's going to be available there is only what you put into the container. This is preferred to running it inside any other container and then &lt;code&gt;docker pull&lt;/code&gt;ing the image; it saves a bit of time. But then it runs the source checkout &lt;em&gt;in the container&lt;/em&gt; too. &lt;a href="https://github.com/actions/checkout" rel="noopener noreferrer"&gt;This&lt;/a&gt; is a clever piece of software: it will run no matter what; it will download your source using its own devices... But we need to checkout the actual git repository, because we will need to push to it afterwards. This is why the container uses this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libcurl4-openssl-dev r-cran-ggplot2 libssl-dev r-cran-httr git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Installing &lt;code&gt;git&lt;/code&gt; where it, in principle, would not have been needed. If present, &lt;code&gt;actions/checkout&lt;/code&gt; uses git, and then we have our repo available for committing and pushing and whatever!&lt;/p&gt;

&lt;p&gt;And it's also got another goodie: &lt;code&gt;bash&lt;/code&gt;. By default, the shell used in a GitHub step is &lt;code&gt;sh&lt;/code&gt;. You can change that, however (yes, &lt;a href="https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#custom-shell" rel="noopener noreferrer"&gt;including Perl&lt;/a&gt;, and yes, I added that piece to the documentation through a PR), and since doing the expression and checking with shell was a bit of a nuisance (and no, we were not going to install Perl in that container (although, come to think of it, it might be there already...))&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2gzqbhy49o0w5knllot9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2gzqbhy49o0w5knllot9.png" alt="Yes, Perl is also there..."&gt;&lt;/a&gt;&lt;br&gt;
... Anyway, an alternative way of just showing off that you know not only bash, but the difference between the syntax of bash and plain &lt;code&gt;sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This new version &lt;a href="https://github.com/JJ/cv/actions/runs/665606623" rel="noopener noreferrer"&gt;takes less than a minute&lt;/a&gt;, with the bulk of it going to downloading and setting up the container&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With a bit of more over-engineering, we could create a slimmed-down R container... Maybe in the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With that, your just-in-time generated &lt;a href="http://jj.github.io/cv/DOWNLOAD" rel="noopener noreferrer"&gt;CV is ready for downloading&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Was it worth the while?
&lt;/h2&gt;

&lt;p&gt;Well, creating a CV is positively boring. Using what you know to make it prettier or automatize its generation, and make it also a bit more entertaining, is definitely worth the while. It also shows craftsmanship, and a passion for optimization.&lt;/p&gt;

&lt;p&gt;Also a bit of a tendency to overengineer stuff that, again, could be created using a wordprocessor. But that's not always bad, right?&lt;/p&gt;

</description>
      <category>r</category>
      <category>docker</category>
      <category>cv</category>
      <category>career</category>
    </item>
    <item>
      <title>How to totally over-engineer your CV in a few easy steps</title>
      <dc:creator>Juan Julián Merelo Guervós</dc:creator>
      <pubDate>Fri, 19 Mar 2021 09:32:20 +0000</pubDate>
      <link>https://dev.to/jj/how-to-totally-over-engineer-your-cv-in-a-few-easy-steps-6ha</link>
      <guid>https://dev.to/jj/how-to-totally-over-engineer-your-cv-in-a-few-easy-steps-6ha</guid>
      <description>&lt;p&gt;Once you need (or don't really need) to create a CV, do it real good, making it show off a few skills. Instead of a static file, make it a dynamic showcase of a few things that you have learned in your career and that might be interesting for the job you've applied to.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Or simply show that you like overengineering something that's mostly done using a word processor. Anyway.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Anyway, here are the yaks I shaved when creating &lt;a href="https://jj.github.io/cv"&gt;this CV&lt;/a&gt;, which, just in case you wondered, is my (more or less real) brief resume, practically up to date. It's got a free license, so feel free to fork the repo (and also add a few pieces more of over-engineered stuff).&lt;/p&gt;

&lt;h2&gt;
  
  
  Use (or adapt) your own CV template
&lt;/h2&gt;

&lt;p&gt;There are many LaTeX templates out there you can use. What they essentially do is pump up headers, and of course give the finished CV a specific appearance. Programming LaTeX is exactly in 0 job postings, but picking up something in a totally unknown language and adapting it to your needs is indeed a nice skill. Anyway, &lt;a href="https://github.com/JJ/cv/blob/master/friggeri-jj-cv.cls"&gt;here's the result&lt;/a&gt; where basically a few fonts and colors were changed, so that they're adapted to modern Ubuntu packages, mostly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check out &lt;a href="https://github.com/JJ/cv/blame/master/friggeri-jj-cv.cls"&gt;git blame&lt;/a&gt; just in case you want to see exactly what was changed, and maybe &lt;a href="https://www.overleaf.com/latex/templates/friggeri-cv-template/hmnchbfmjgqh"&gt;use the original&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Use XeLaTeX and biber, not LaTeX
&lt;/h2&gt;

&lt;p&gt;Well, that came with the original template, so, well, it wasn't really much of a choice. What's nice about using &lt;code&gt;biber&lt;/code&gt; is that it's a Perl script, and you need to get the correct CPAN package and Perl version to get it working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate the PDF automatically
&lt;/h2&gt;

&lt;p&gt;It would be a waste of computing time to type &lt;code&gt;make pdf&lt;/code&gt; or anything like that to actually generate it, right? Actually, it's more complicated than that. Best practices advise you to &lt;em&gt;not&lt;/em&gt; include generated stuff in your repo (mostly), and this PDF would clearly be a &lt;em&gt;product&lt;/em&gt; of your &lt;em&gt;application&lt;/em&gt; (the CV generator). So we could as well create real releases, that have the CV attached to them. We do so with this &lt;a href="https://github.com/JJ/cv/blob/master/.github/workflows/generate.yaml"&gt;Github action&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Generate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;CV"&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;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;generate-cv&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;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;Checkout&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;
        &lt;span class="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;Install tools&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.github/workflows/latex-install.sh&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;Generate pdf&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./generate_cv.sh&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;Go to main branch and generate thumbnail&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;git status &amp;amp;&amp;amp; git checkout master &amp;amp;&amp;amp; thumbpdf --makepng --verbose --noclean cv.pdf &amp;amp;&amp;amp; mv thb1.png cv.png&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;Publishes CV&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;ncipollo/release-action@v1&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;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cv.pdf"&lt;/span&gt;
          &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Fresh CV generated&lt;/span&gt;
          &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works only when you tag a repository (and remember to &lt;code&gt;git push &amp;amp;&amp;amp; git push --tags&lt;/code&gt; so that the actual commit that's tagged is used). It needs to check out the whole repo, and not only download the tagged code, for reasons that will be clear a bit later. But we need to &lt;a href="https://github.com/JJ/cv/blob/master/.github/workflows/latex-install.sh"&gt;install the tools&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;texlive-xetex biber fonts-goudybookletter texlive-fonts-extra  texlive-science-doc texlive-science fonts-adf-accanthis fonts-smc-gayathri ghostscript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essentially it's installing a bunch of packages, including the fonts that were declared in the LaTeX class file at the beginning. And &lt;code&gt;ghostscript&lt;/code&gt;, whose need we'll see a bit later. This takes a good while, around 4 minutes. So it's ripe for optimization, one way or the other. We could dockerize it, but we'll save dockerization for later. We could also use a cache. And that we tried.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We really &lt;em&gt;over-engineered&lt;/em&gt; this one...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://github.com/JJ/cv/blob/ab58acfbdc88a1b1e94d5d8eaa55dfc3308a1c2f/.github/workflows/generate.yaml"&gt;This version&lt;/a&gt; uses &lt;a href="https://github.com/marketplace/actions/cache-anything-new"&gt;a caching github action step&lt;/a&gt;. Wasn't really worth the while. It took like a minute to check for cache hits, and around two minutes to actually download the cache in case of cache misses, not to mention around half an hour to actually set up the cache in the first place. All that to shave a bare minute... Not really worth the while. That way we can change more things in the Latex setup if we want.&lt;/p&gt;

&lt;p&gt;Actually generating the PDF is &lt;a href="https://github.com/JJ/cv/blob/ab58acfbdc88a1b1e94d5d8eaa55dfc3308a1c2f/generate_cv.sh"&gt;relatively straightforward&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xelatex cv.tex
biber cv
xelatex cv.tex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Except that, remember, &lt;code&gt;biber&lt;/code&gt; uses Perl. So how come this work? Well, perl is installed in all Linux runners in GitHub actions. We'll see more of that later on... So this works without a glitch.&lt;/p&gt;

&lt;p&gt;After that, we can create the release. Jump over the next step (which is there for historical reasons) and go to the next one, which simply &lt;code&gt;Publishes CV&lt;/code&gt; using another external github action. Very simply, it picks the generated artifact, &lt;code&gt;cv.pdf&lt;/code&gt;, creates the release and attaches it to it.&lt;/p&gt;

&lt;p&gt;But we need to generate a thumbnail, because of course, we need a thumbnail to link to the actual CV. I said this was going to be overengineered, right? This will be used in the index page to point to the actual page that will be used to download the CV. This does the trick:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status 
  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git checkout master 
  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; thumbpdf &lt;span class="nt"&gt;--makepng&lt;/span&gt; &lt;span class="nt"&gt;--verbose&lt;/span&gt; &lt;span class="nt"&gt;--noclean&lt;/span&gt; cv.pdf 
  &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mv &lt;/span&gt;thb1.png cv.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Actually, the last two lines do; the first are there to go from the tag to the main branch. After exploring lots of different utilities, like &lt;code&gt;convert&lt;/code&gt;, part of ImageMagick, which is actually broken, &lt;code&gt;thumbpdf&lt;/code&gt;, which is installed alongside LaTeX but actually uses &lt;code&gt;ghostscript&lt;/code&gt; underneath, works nicely, generating a PNG for each page. The last command calls it the way we want.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;thumbpdf&lt;/code&gt; is also written in Perl. Now you start to understand why Perl is installed everywhere (and why it's always a &lt;em&gt;good thimg&lt;/em&gt; (TM) to know how to use it. As a matter of fact, I had to check out the source to find out what was missing after having it fail with an obscure error message.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is actually a generated file, and we've included it in the repo. Well, why not. It's a small file, images (like this, which is actually an icon) will be needed for the page (and you can see it above). But just &lt;em&gt;having&lt;/em&gt; it in this local copy of the repo is not enough. We will need to push it to the repo.&lt;/p&gt;

&lt;p&gt;But before, we'll also create other things we need to push to the repo. The CV is going to be uploaded to a different URL every time a release is created. Although the template for such URL is straightforward, and can be found by simply navigating the site, we need something much simpler: generate a page with a link that you can simply click to download de CV. Perl again to the rescue, in these two final steps of the 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="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;Generate URL&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.github/workflows/generate-download-url.pl &amp;gt; DOWNLOAD.md&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;Pushes thumbnail and URL file.&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;USER_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JJCVBot&lt;/span&gt;
          &lt;span class="na"&gt;EMAIL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jjmerelo@gmail.com&lt;/span&gt;
          &lt;span class="na"&gt;MY_COMMIT_MSG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Updates download doc&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;curl https://raw.githubusercontent.com/JJ/perl-GitHub-Actions/main/bin/commit-push.pl | perl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first script effectively generates the markdown file that will be checked in, and that will be rendered to web and used effectively as a signpost to download the CV. Here's that iw does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;constant&lt;/span&gt; &lt;span class="s"&gt;URL_FILE&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;download_cv_url.txt&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;Git&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;v5&lt;/span&gt;&lt;span class="mf"&gt;.14&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;# For say&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;Git&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;Directory&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="p"&gt;');&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$repo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;

&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;pop&lt;/span&gt; &lt;span class="nv"&gt;@tags&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$download_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://github.com/JJ/cv/releases/download/&lt;/span&gt;&lt;span class="si"&gt;$tag&lt;/span&gt;&lt;span class="s2"&gt;/cv.pdf&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;

&lt;span class="nb"&gt;open&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$url_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; &lt;span class="nv"&gt;URL_FILE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;print&lt;/span&gt; &lt;span class="nv"&gt;$url_file&lt;/span&gt; &lt;span class="nv"&gt;$download_url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;close&lt;/span&gt; &lt;span class="nv"&gt;$url_file&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;say&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;&amp;lt;EOC;
# Download CV
[Download latest version of CV in PDF]($download_url)
EOC
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First interesting thing about this is that it's run directly on the Github Actions runner, no external dependencies. It &lt;code&gt;use&lt;/code&gt;s &lt;code&gt;Git&lt;/code&gt;, a Perl module installed alongside &lt;code&gt;git&lt;/code&gt; proper. That's used to get the tags in the repo (remember, we checked out the whole repo) and use the last one to generate the URL that's printed to a file, and also to the &lt;code&gt;DOWNLOAD.md&lt;/code&gt; markdown file. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The tag that triggered the action is actually available as an environment variable, so we could have used that. However, we didn't know in principle if we were going to use that kind of trigger. This script will work even if we trigger the action in some other way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finally, we're checking this in to the repository; there are three files: the download URL (which we could, in principle, use like this: &lt;code&gt;wget $(wget raw_download_url)&lt;/code&gt;), the thumbnail and &lt;code&gt;DOWNLOAD.md&lt;/code&gt;. We will use an external Perl script to do so.&lt;/p&gt;

&lt;p&gt;This script is part of the &lt;a href="https://metacpan.org/release/GitHub-Actions"&gt;&lt;code&gt;GitHub::Actions&lt;/code&gt;&lt;/a&gt; perl module, which contains a few utility functions (and scripts) to use within your, you guessed it, Github Actions. Since GHAs use environment variables to communicate, it takes the username and email (used to set the git configuration locally for the commit; you should use your own email if you want the commit to be identified as yours in GitHub) and the commit message. Here's the (rater uncomplicated) script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;v5&lt;/span&gt;&lt;span class="mf"&gt;.14&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;Git&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;Git&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;repository&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;Directory&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="p"&gt;');&lt;/span&gt;

&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$user_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="p"&gt;{'&lt;/span&gt;&lt;span class="s1"&gt;USER_NAME&lt;/span&gt;&lt;span class="p"&gt;'}&lt;/span&gt; &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="p"&gt;{'&lt;/span&gt;&lt;span class="s1"&gt;GITHUB_ACTOR&lt;/span&gt;&lt;span class="p"&gt;'};&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="p"&gt;{'&lt;/span&gt;&lt;span class="s1"&gt;EMAIL&lt;/span&gt;&lt;span class="p"&gt;'};&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$commit_msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="p"&gt;{'&lt;/span&gt;&lt;span class="s1"&gt;MY_COMMIT_MSG&lt;/span&gt;&lt;span class="p"&gt;'}&lt;/span&gt; &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No message&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$repo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-s&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$repo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;config&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--global&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user.email&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nv"&gt;$repo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;config&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--global&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user.name&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="nv"&gt;$user_name&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nv"&gt;$repo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-am&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="nv"&gt;$commit_msg&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nv"&gt;$repo&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;command&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;push&lt;/span&gt;&lt;span class="p"&gt;');&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, it's perusing the &lt;code&gt;Git&lt;/code&gt;  module which we know is available, there are a whole lot of &lt;a href="https://gist.github.com/JJ/edf3a39d68525439978da2a02763d42b"&gt;Perl modules&lt;/a&gt; availables there, doing a whole lot of things. If you can use them, you will save some time because you will not need to &lt;em&gt;pack&lt;/em&gt; them somehow. Anyway, it configures the email and name, commits and push. Couldn't be more straightforward. You could actually do that easily with a shell script, pretty much this way:&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="si"&gt;$(&lt;/span&gt;git status &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
              &lt;/span&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.email &lt;span class="s2"&gt;"jjmerelo@gmail.com"&lt;/span&gt;
              git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.name &lt;span class="s2"&gt;"ÁgilDataBot"&lt;/span&gt;
              .github/workflows/commit-msg.pl | git commit &lt;span class="nt"&gt;-aF&lt;/span&gt; -
              git push
          &lt;span class="k"&gt;else
              &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"🟏 No changes"&lt;/span&gt;
          &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essentially, you will need to avoid making a commit and push if there are no changes, because the step would error (it could happen if you have pushed to an existing tag, for instance, and the cv has not changed).&lt;/p&gt;

&lt;h2&gt;
  
  
  That's it for the resumé
&lt;/h2&gt;

&lt;p&gt;However, there's an important part of the resumé which is your programming timeline. That will need a whole 'nother article, I guess. You can check it out &lt;a href="https://dev.to/jj/how-to-totally-over-engineer-your-cv-part-two-1oip"&gt;in part II of this series&lt;/a&gt;&lt;/p&gt;

</description>
      <category>latex</category>
      <category>githubactions</category>
      <category>perl</category>
    </item>
    <item>
      <title>ALL environment variables available in the Docker Hub build environment</title>
      <dc:creator>Juan Julián Merelo Guervós</dc:creator>
      <pubDate>Mon, 28 Dec 2020 10:05:21 +0000</pubDate>
      <link>https://dev.to/jj/all-environment-variables-available-in-the-docker-hub-build-environment-9j9</link>
      <guid>https://dev.to/jj/all-environment-variables-available-in-the-docker-hub-build-environment-9j9</guid>
      <description>&lt;p&gt;The &lt;a href="https://docs.docker.com/docker-hub/builds/advanced/#environment-variables-for-building-and-testing"&gt;Docker Hub documentation&lt;/a&gt; lists all these environment variables, which are available in your scripts in that environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    SOURCE_BRANCH: the name of the branch or the tag that is currently being tested.
    SOURCE_COMMIT: the SHA1 hash of the commit being tested.
    COMMIT_MSG: the message from the commit being tested and built.
    DOCKER_REPO: the name of the Docker repository being built.
    DOCKERFILE_PATH: the dockerfile currently being built.
    DOCKER_TAG: the Docker repository tag being built.
    IMAGE_NAME: the name and tag of the Docker repository being built. (This variable is a combination of DOCKER_REPO:DOCKER_TAG.)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, there are quite a few more, which might be useful in a certain environment; these were discovered by just printing the environment variables via a Perl script (which, BTW, &lt;a href="https://dev.to/jj/writing-docker-hub-hooks-in-perl-44h2"&gt;you can use in Docker Hub&lt;/a&gt; for your own purposes).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SOURCE_TYPE&lt;/code&gt;: this one seems to contain &lt;code&gt;git&lt;/code&gt;. It might be different if it's pushed directly, or via the API.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PATH&lt;/code&gt;: is the regular system PATH. It contains &lt;code&gt;/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MAX_LOG_SIZE&lt;/code&gt;: it contains 67108864. Not clear if it can be changed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BUILD_CODE&lt;/code&gt;: no idea what this is. It contains a code.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PUSH&lt;/code&gt;: it contains &lt;code&gt;true&lt;/code&gt; if it's been created via PUSH. Probably other values if it's been automatically launched.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GIT_TAG&lt;/code&gt; and &lt;code&gt;GIT_SHA1&lt;/code&gt;: no idea what the first one is, the second is the commit SHA1.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HOSTNAME&lt;/code&gt;: the host it's using to build.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DOCKER_HOST&lt;/code&gt;: the socket the client is using to connect, same as in the local docker installation.&lt;/li&gt;
&lt;li&gt;There's something called &lt;code&gt;SIGNED_URLS&lt;/code&gt;, which holds Amazon urls. No idea what this is, or how it can be useful.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DOCKER_TAG&lt;/code&gt; and &lt;code&gt;CACHE_TAG&lt;/code&gt;, the tag that's being built, like &lt;code&gt;latest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PYTHONUNBUFFERED&lt;/code&gt; is equal to 1.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SOURCE_REPOSITORY_URL&lt;/code&gt; the repository it's being built from.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LOG_BUILD_STEPS&lt;/code&gt; is set to &lt;code&gt;FALSE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DOCKER_CFG&lt;/code&gt; contains information on the user, including user tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Out of all these, you can probably use &lt;code&gt;PUSH&lt;/code&gt; or even &lt;code&gt;GIT_SHA1&lt;/code&gt; to introspect what's being built, and maybe take some actions. For instance, along with &lt;a href="https://github.com/docker/hub-feedback/issues/2062"&gt;information on the last build&lt;/a&gt; you could create a script that built only those Dockerfiles that have changed.&lt;/p&gt;

</description>
      <category>dockerhub</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
