<?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: Gavin Panella</title>
    <description>The latest articles on DEV Community by Gavin Panella (@allenap).</description>
    <link>https://dev.to/allenap</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%2F36722%2F580f95f6-15ea-4151-b882-bba12c2298a1.jpg</url>
      <title>DEV Community: Gavin Panella</title>
      <link>https://dev.to/allenap</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/allenap"/>
    <language>en</language>
    <item>
      <title>What's in a Makefile?</title>
      <dc:creator>Gavin Panella</dc:creator>
      <pubDate>Tue, 28 Jan 2020 08:26:36 +0000</pubDate>
      <link>https://dev.to/allenap/what-s-in-a-makefile-3n64</link>
      <guid>https://dev.to/allenap/what-s-in-a-makefile-3n64</guid>
      <description>&lt;p&gt;Rules like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  target: [prerequisite] [...]
      shell command
      another shell \
         command on multiple lines
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The target is the name of a file that can be built by those shell commands. If you run &lt;code&gt;make target&lt;/code&gt; and the target is older than any of the prerequisite files, or doesn't exist, Make runs the shell commands. The prerequisites are also file names; Make will look for rules to build them too, recursively. If all of them are up to date, Make will say &lt;em&gt;`target' is up to date&lt;/em&gt; and exit without running any commands.&lt;/p&gt;

&lt;p&gt;Some things that might trip you up at first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Those shell commands &lt;strong&gt;must&lt;/strong&gt; be indented by a proper tab character.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each line of shell commands is $-expanded by Make &lt;strong&gt;before&lt;/strong&gt; being passed to the shell. Make expands &lt;code&gt;$(NAME)&lt;/code&gt; and &lt;code&gt;${NAME}&lt;/code&gt; but &lt;code&gt;$NAME&lt;/code&gt; would work like &lt;code&gt;${N}AME&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Each logical shell command is run in a separate shell. The example above would result in something like the following:&lt;br&gt;
&lt;code&gt;&lt;br&gt;
$(SHELL) -c 'shell command' &amp;amp;&amp;amp;&lt;br&gt;
  $(SHELL) -c 'another shell command on multiple lines'&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
This means that &lt;em&gt;shell&lt;/em&gt; variables and functions are not passed from one&lt;br&gt;
command to the next.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Execution stops at the first failure.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the symbols in a Makefile look weird, check the &lt;a href="https://www.gnu.org/software/make/manual/html_node/Quick-Reference.html"&gt;GNU &lt;code&gt;make&lt;/code&gt; Quick Reference&lt;/a&gt;. For convenience here are a few that are more common:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symbol&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$@&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The file name of the target.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The name of the first prerequisite.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$^&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The names of all the prerequisites.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$(@D)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The directory part of &lt;code&gt;$@.&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$(@F)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The file-within-directory (&lt;code&gt;basename&lt;/code&gt;) part of &lt;code&gt;$@&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$$&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A literal dollar sign. Useful when you really need to reference a shell variable.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Make also has variables, recipes, functions, and conditional evaluation, for example, but everything is based around rules like the one at the top. Check out the &lt;a href="https://www.gnu.org/software/make/manual/html_node/index.html#Top"&gt;GNU &lt;code&gt;make&lt;/code&gt; manual&lt;/a&gt; to learn more. There are other variants of Make, but GNU &lt;code&gt;make&lt;/code&gt; seems to be prevalent. Its manual calls out where behaviour is specific to GNU so it's a decent starting point for whatever variant you're working with.&lt;/p&gt;

&lt;p&gt;Good luck!&lt;/p&gt;

</description>
      <category>make</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Some direnv best practices. Actually just one.</title>
      <dc:creator>Gavin Panella</dc:creator>
      <pubDate>Mon, 04 Nov 2019 13:24:03 +0000</pubDate>
      <link>https://dev.to/allenap/some-direnv-best-practices-actually-just-one-4864</link>
      <guid>https://dev.to/allenap/some-direnv-best-practices-actually-just-one-4864</guid>
      <description>&lt;p&gt;I use &lt;a href="https://direnv.net/"&gt;direnv&lt;/a&gt; to configure the environment in most projects I'm working on. In &lt;a href="https://www.noredink.com/jobs"&gt;my day job&lt;/a&gt; a few projects use direnv to load &lt;a href="https://nixos.org/nix/"&gt;Nix&lt;/a&gt; environments for developers on macOS and Linux, and I did some of the work to make that a good experience. It's in doing that that I've learned what can help, and what might also help you.&lt;/p&gt;

&lt;h1&gt;
  
  
  Be quick or be dead
&lt;/h1&gt;

&lt;p&gt;There's one rule that should never, ever be broken, and it's almost all I'm going to talk about in this post, and it is: &lt;strong&gt;do not block&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By this I mean that &lt;code&gt;.envrc&lt;/code&gt;&lt;sup id="fnref1"&gt;1&lt;/sup&gt; should run in a few hundred milliseconds, no more. 500ms is pushing it. That's not long. There's no way to instantiate a Nix shell&lt;sup id="fnref2"&gt;2&lt;/sup&gt; in that time, for example.&lt;/p&gt;

&lt;p&gt;So don't. Do not call &lt;code&gt;nix-shell&lt;/code&gt; in &lt;code&gt;.envrc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The same rule holds if you're using NPM – maybe you're tempted to put &lt;code&gt;npm install&lt;/code&gt; into &lt;code&gt;.envrc&lt;/code&gt; for example – or another package management or build tool. Think twice before using &lt;code&gt;curl&lt;/code&gt; or &lt;code&gt;wget&lt;/code&gt;. The rule always applies: don't do anything in &lt;code&gt;.envrc&lt;/code&gt; that's not going to return quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why blocking is a problem
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd some_directory
direnv: loading .envrc
direnv: ([direnv export bash]) is taking a while to execute. Use CTRL-C to give up.
... a long time passes ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your interactive shell is blocked. You wanted to pop in to take a look at &lt;code&gt;README.txt&lt;/code&gt; but now you have to wait.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bright idea #1: open another terminal and &lt;code&gt;cd some_directory&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Whereupon direnv &lt;em&gt;starts a second long-running process&lt;/em&gt;. Facepalm: &lt;em&gt;should've guessed that&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bright idea #2: hit ctrl-c to give up
&lt;/h3&gt;

&lt;p&gt;The background process was killed. Good. You type &lt;code&gt;git pull&lt;/code&gt; to make sure you're up to date.&lt;/p&gt;

&lt;p&gt;You have just entered a command: &lt;code&gt;git pull&lt;/code&gt;. If direnv is configured correctly its hook will be run just before your shell displays the next prompt. direnv will find the &lt;code&gt;.envrc&lt;/code&gt; in the directory and run it, &lt;em&gt;starting another long-running process&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You have been eaten by a grue.&lt;/p&gt;

&lt;p&gt;A thing to note: hitting ctrl-c doesn't always work&lt;sup id="fnref3"&gt;3&lt;/sup&gt;. The long-running process sometimes keeps running in the background, writing to the terminal, using RAM, CPU cores, and your battery life. At this point you might use &lt;code&gt;pkill&lt;/code&gt; on the background process.&lt;/p&gt;

&lt;p&gt;You have just entered a command: &lt;code&gt;pkill some_thing&lt;/code&gt;. If direnv is configured correctly ...&lt;/p&gt;

&lt;p&gt;Long story short: you have been eaten by a grue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bright idea #3: look at &lt;code&gt;README.txt&lt;/code&gt; from your editor
&lt;/h3&gt;

&lt;p&gt;You open &lt;code&gt;README.txt&lt;/code&gt; and your editor locks up. You wonder why your editor isn't responding. You force kill it and try again. It locks up. You reboot and try again. It locks ...&lt;/p&gt;

&lt;p&gt;Facepalm #2: you installed the direnv plugin to your editor. Your editor has triggered direnv into &lt;em&gt;starting yet another long-running process&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bright idea #4: get angry
&lt;/h3&gt;

&lt;p&gt;This actually works, but is ultimately unfulfilling and doesn't solve the underlying problem.&lt;/p&gt;

&lt;p&gt;Anyway, &lt;code&gt;rm some_directory/.envrc&lt;/code&gt; or &lt;code&gt;direnv deny some_directory&lt;/code&gt; and you can freely come and go, but you're not getting the benefit of that direnv integration you signed up for. It might be all that you can do when working with someone else's code though.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to do it instead
&lt;/h2&gt;

&lt;p&gt;Have a &lt;strong&gt;separate&lt;/strong&gt; process to build your environment. With Nix, for example, you could write a build script like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
nix-shell &lt;span class="nt"&gt;--run&lt;/span&gt; &lt;span class="s1"&gt;'direnv dump &amp;gt; .envrc.cache'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your &lt;code&gt;.envrc&lt;/code&gt; could be as simple as:&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;source&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;direnv apply_dump .envrc.cache&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This change, as basic as it looks, gives you back control:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can &lt;code&gt;cd&lt;/code&gt; into any directory without worry that you'll trigger a build.&lt;/li&gt;
&lt;li&gt;You can open any file in your editor without worrying that it'll freeze.&lt;/li&gt;
&lt;li&gt;You are unlikely to run the build script multiple times concurrently (and ctrl-c will probably work if you do).&lt;/li&gt;
&lt;li&gt;You choose when to build, e.g. when you're not running on battery power.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is more you can do with this. For example, the build script above will show an error if you haven't created &lt;code&gt;.envrc.cache&lt;/code&gt; yet. You could make it instead prompt&lt;sup id="fnref4"&gt;4&lt;/sup&gt; the user to run the build script.&lt;/p&gt;

&lt;h3&gt;
  
  
  A couple of other things
&lt;/h3&gt;

&lt;p&gt;If you use &lt;code&gt;ssh-agent&lt;/code&gt; then the cache will contain a value for &lt;code&gt;SSH_AUTH_SOCK&lt;/code&gt;. This isn't sensitive but it will get out of date if you reboot, say. The symptom is that &lt;code&gt;ssh&lt;/code&gt; and commands that use &lt;code&gt;ssh&lt;/code&gt;, like &lt;code&gt;git&lt;/code&gt;, will always prompt you for your password.&lt;/p&gt;

&lt;p&gt;The cache will also contain values for &lt;code&gt;DIRENV_DIFF&lt;/code&gt;, &lt;code&gt;DIRENV_DIR&lt;/code&gt;, and &lt;code&gt;DIRENV_WATCHES&lt;/code&gt;. These are direnv's bookkeeping records. Applying the dump without filtering these variables out can cause weird behaviour.&lt;/p&gt;

&lt;p&gt;You can update your build script to address both of these problems like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
nix-shell &lt;span class="nt"&gt;--run&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'unset ${!SSH_@} ${!DIRENV_@} &amp;amp;&amp;amp; direnv dump &amp;gt; .envrc.cache'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At NoRedInk we go further and cache only the &lt;em&gt;difference&lt;/em&gt; in the environment so that loading the cache doesn't clobber environment variables unrelated to the project. We also detect if the cache is stale and prompt the user to recreate it.&lt;/p&gt;

&lt;h1&gt;
  
  
  There's more to talk about
&lt;/h1&gt;

&lt;p&gt;Like: forking background processes from &lt;code&gt;.envrc&lt;/code&gt; (be prepared to be eaten by a grue many times before you get it right), tools like &lt;a href="https://github.com/target/lorri"&gt;lorri&lt;/a&gt;, and perhaps some more about NoRedInk's tooling, but there's enough in this post already. I hope it's useful. Please like and subscribe :-)&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;direnv looks for a &lt;code&gt;.envrc&lt;/code&gt; file in a directory you &lt;code&gt;cd&lt;/code&gt; into. If it finds one it interprets it as a Bash script, then copies all of the exported environment variables into your shell's environment (which doesn't have to be Bash). It does some bookkeeping too so that it knows how to restore the previous environment when you &lt;code&gt;cd&lt;/code&gt; out of that directory. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;The &lt;code&gt;nix-shell&lt;/code&gt; command is how you start a Nix shell environment with the packages and configuration you've asked for in a &lt;code&gt;shell.nix&lt;/code&gt; file. How that works is &lt;em&gt;way&lt;/em&gt; out of scope for this post. Suffice it to say that &lt;code&gt;nix-shell&lt;/code&gt; almost never returns in less than ~5 seconds, and can take &lt;em&gt;hours&lt;/em&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Maybe this is a bug in direnv, something to do with process groups or some such. Thing is, it's a problem that exists and we have to account for it. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;Note that I wrote "prompt", not "run the build script" or "ask the user if they want to run the build script". Either of those would block. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>direnv</category>
      <category>nix</category>
    </item>
  </channel>
</rss>
