<?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: Austin S. Hemmelgarn</title>
    <description>The latest articles on DEV Community by Austin S. Hemmelgarn (@ahferroin7).</description>
    <link>https://dev.to/ahferroin7</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%2F175476%2Fd0140652-118d-4eb2-a165-0184a260ad50.png</url>
      <title>DEV Community: Austin S. Hemmelgarn</title>
      <link>https://dev.to/ahferroin7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ahferroin7"/>
    <language>en</language>
    <item>
      <title>The magic of `git worktree` — How I multitask despite tests taking forever to run.</title>
      <dc:creator>Austin S. Hemmelgarn</dc:creator>
      <pubDate>Tue, 13 Dec 2022 02:45:57 +0000</pubDate>
      <link>https://dev.to/ahferroin7/the-magic-of-git-worktree-how-i-multitask-despite-tests-taking-forever-to-run-5fe4</link>
      <guid>https://dev.to/ahferroin7/the-magic-of-git-worktree-how-i-multitask-despite-tests-taking-forever-to-run-5fe4</guid>
      <description>&lt;p&gt;As a programmer, sometimes there are situations where you need to jump to a different task when you’re right in the middle of something. Most decent VCS software provides a selection of ways to approach this, ranging from things like patch queues, to branches, to any number of other mechanisms.&lt;/p&gt;

&lt;p&gt;However, one of the big limitations of these mechanisms is that they require you to change out the entire working tree, so you can’t use them if your existing task happens to require the working tree, such as running a build or tests, or if you need to preserve the state of an unclean working tree.&lt;/p&gt;

&lt;p&gt;You can kind of work around this by creating another clone of the repository, but this has a couple of major limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Special effort needs to be made if you need to keep the two working trees in sync.&lt;/li&gt;
&lt;li&gt;Special effort needs to be made to keep track of exactly which copy of the repo you’re working with.&lt;/li&gt;
&lt;li&gt;Twice as much disk space is required.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With &lt;code&gt;git&lt;/code&gt;, you can avoid most of the extra disk space by using &lt;code&gt;git clone --shared&lt;/code&gt; to clone the local repository, but this is still a wholly separate repo, so it still runs into the other two issues.&lt;/p&gt;

&lt;p&gt;Thankfully &lt;code&gt;git&lt;/code&gt; has a wonderful solution to this...&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter &lt;code&gt;git worktree&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;One of the lesser known features of &lt;code&gt;git&lt;/code&gt; is that it supports having multiple working trees attached to the same repository. Each git repository (except for bare repositories) has a ‘main’ working tree, which is the one that actually has the &lt;code&gt;.git&lt;/code&gt; directory for the repository, plus zero or more ‘linked’ working trees, which use a file called &lt;code&gt;.git&lt;/code&gt; to link back to the &lt;code&gt;.git&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;To manage these linked working trees, git provides the &lt;code&gt;git worktree&lt;/code&gt; subcommand. Creating a new linked working tree that has a specific branch checked out is simple as running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree add /path/to/worktree branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of a branch, you can specify any tag, commit, or other ref to check out that specific ref. If you don’t specify a ref to checkout, then a new branch will be created automatically from the currently checked-out commit with a name based on the basename of the new worktree.&lt;/p&gt;

&lt;p&gt;Once created, you can change to a linked working tree, and then use it (mostly) just like a regular git repository. The key difference is that it shares a &lt;code&gt;.git&lt;/code&gt; directory with the main working tree. This means that if you create a new branch in the linked working tree and add commits to it, that same branch and commits will be immediately accessible in the main working tree and any other linked working trees.&lt;/p&gt;

&lt;p&gt;Worktrees can be listed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Moved:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree move /old /new
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or removed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git worktree remove /path/to/worktree
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  But how to use it?
&lt;/h2&gt;

&lt;p&gt;Say as an example that you’ve got a build running in your main copy of the repository, but need to hop to a different branch to work on something else in a different branch while waiting for the build to finish. You can easily do this using &lt;code&gt;git worktree&lt;/code&gt; from a different shell as follows:&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="nv"&gt;WORKTREE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/path/to/tree"&lt;/span&gt;
git worktree add &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WORKTREE_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; branch
&lt;span class="nb"&gt;pushd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WORKTREE_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="c"&gt;# make needed changes and commit them...&lt;/span&gt;
&lt;span class="nb"&gt;popd
&lt;/span&gt;git worktree remove &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WORKTREE_PATH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same approach can be used to create temporary work trees for running tests (I do this on a regular basis, especially when dealing with cross-architecture builds where things need to run under QEMU userspace emulation for testing), or as a way to provide two full copies of different versions of a source tree to compare using some tool that doesn’t integrate with &lt;code&gt;git&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  OK, what’s the catch?
&lt;/h2&gt;

&lt;p&gt;As amazingly useful as &lt;code&gt;git worktree&lt;/code&gt; is, there are still a few major limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;.git&lt;/code&gt; file in a linked worktree embeds the path of the main worktree. Because of this, if you move the main worktree, any linked worktrees will become unusable until you run &lt;code&gt;git worktree repair&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Similarly, the main worktree’s &lt;code&gt;.git&lt;/code&gt; directory embeds the paths to any linked worktrees. This means that if you want to move a linked worktree, you either need to use &lt;code&gt;git worktree move&lt;/code&gt;, or you need to run &lt;code&gt;git worktree repair&lt;/code&gt; in the linked worktree after you move it.&lt;/li&gt;
&lt;li&gt;If for some reason both the main &lt;em&gt;and&lt;/em&gt; the linked worktrees get moved at the same time, you have to run &lt;code&gt;git worktree repair&lt;/code&gt; in the main worktree, and pass it the new paths to each linked worktree.&lt;/li&gt;
&lt;li&gt;While &lt;code&gt;git&lt;/code&gt; technically supports per-worktree configuration, it’s tricky to use, and there are a number of gotchas.&lt;/li&gt;
&lt;li&gt;Even if you have multiple worktrees, you cannot safely check out the same branch more than once.&lt;/li&gt;
&lt;li&gt;Support for submodules is a bit lacking. It mostly works, but you have to force removal when removing a worktree that contains checked-out submodules, and have to manually move worktrees that contain submodules.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;More details can be found &lt;a href="https://git-scm.com/docs/git-worktree" rel="noopener noreferrer"&gt;in the git documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>watercooler</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Skip CI stages in Travis based on what files changed.</title>
      <dc:creator>Austin S. Hemmelgarn</dc:creator>
      <pubDate>Tue, 24 Dec 2019 00:59:17 +0000</pubDate>
      <link>https://dev.to/ahferroin7/skip-ci-stages-in-travis-based-on-what-files-changed-3a4k</link>
      <guid>https://dev.to/ahferroin7/skip-ci-stages-in-travis-based-on-what-files-changed-3a4k</guid>
      <description>&lt;p&gt;At my new job, I've ended up partially responsible for the CI pipeline for our flagship product. One of the big complaints from both our internal devs and our FOSS contributors is how long CI builds (and thus the CI checks on pull requests on GitHub) take to complete.&lt;/p&gt;

&lt;p&gt;Digging into this, I pretty quickly realized that the main culprit was our unit testing, which includes stress tests for one of the components which take almost 15 minutes to run, even if the build is for a PR or commit that doesn't even touch that code.&lt;/p&gt;

&lt;p&gt;So, I set about looking into how to skip those tests when the changes being tested had nothing to do with that component.&lt;/p&gt;

&lt;h2&gt;
  
  
  A First Look
&lt;/h2&gt;

&lt;p&gt;We currently use Travis CI to do our CI/CD builds. Travis actually provides you a rather significant amount of control over what jobs run as part of a build based on what you're doing. You can match on quite a few things, including stuff like whether the build is for a PR or not, what distribution the build is being done on, and even whether the repo the build is for is a fork or not.&lt;/p&gt;

&lt;p&gt;However, there's no way from the conditionals to check anything about the contents of the branch being built (you can match on branch name or commit message, though those aren't that useful for our purposes). Given this, we need to do the checks inside the scripts for the unit testing using data from the build environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Matter of Depth
&lt;/h2&gt;

&lt;p&gt;Before going further into figuring out how to do this, there's a rather important detail about Travis that needs to be taken into account. By default, when Travis clones a Git repository, it does a shallow clone with only the most recent commit. This provides a significant performance improvement to the cloning process when dealing with large repos, but it means that you can't find any information about anything &lt;em&gt;except&lt;/em&gt; the most recent commit, which is an issue when trying to determine what files have changed.&lt;/p&gt;

&lt;p&gt;Thankfully, disabling this and getting a deep clone of the source repo with the full history is rather easy, you just need to add the following to any job that you need to inspect the history with:&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;git&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could also specify a number between 1 and 50 (Travis imposes this 50 commit limit, not &lt;code&gt;git&lt;/code&gt;) instead of &lt;code&gt;false&lt;/code&gt; to get a clone with that number of commits. For most use cases, just specifying 50 there should be more than enough. However, the performance difference between fetching the 50 most recent commits and the whole repo is rather minimal unless you have an absolutely huge history on the branch you're checking against, so it's safer to just disable the shallow cloning altogether.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Build Environment
&lt;/h2&gt;

&lt;p&gt;Back to the task at hand, Travis provides a &lt;em&gt;huge&lt;/em&gt; amount of info in the build environment in the form of environment variables (see the full list &lt;a href="https://docs.travis-ci.com/user/environment-variables/"&gt;here&lt;/a&gt;). This includes a lot of the same stuff that you can match in conditionals, but also includes quite a bit more than that, including such things as what compiler is being used, what CPU architecture is being used, and even the URL for the build logs.&lt;/p&gt;

&lt;p&gt;Rather helpfully, there is an environment variable provided by Travis that tells you what range of commits you're testing with this build, called &lt;code&gt;TRAVIS_COMMIT_RANGE&lt;/code&gt;. This provides the commit range being tested as two shortened commit hashes separated by &lt;code&gt;...&lt;/code&gt;. Based on this, if you're using &lt;code&gt;git&lt;/code&gt; for version control, the following snippet of &lt;code&gt;bash&lt;/code&gt; code will give you a list of all the files that changed for this build, one per line, in the variable &lt;code&gt;CHANGED_FILES&lt;/code&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="c"&gt;# `git` expects a `..` delimiter between commits, not `...`&lt;/span&gt;
&lt;span class="nv"&gt;COMMIT_RANGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TRAVIS_COMMIT_RANGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'.'&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 1,4 &lt;span class="nt"&gt;--output-delimiter&lt;/span&gt; &lt;span class="s1"&gt;'..'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;CHANGED_FILES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMMIT_RANGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have that, you can easily use any of a number of tools to check the list of changed files against a set of matching conditions to decide on what to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Few Small Issues
&lt;/h2&gt;

&lt;p&gt;The above code works great most of the time. However, there are three caveats to using &lt;code&gt;TRAVIS_COMMIT_RANGE&lt;/code&gt; that can cause the above code to fail:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If the build is for a newly pushed branch, &lt;code&gt;TRAVIS_COMMIT_RANGE&lt;/code&gt; will be empty.&lt;/li&gt;
&lt;li&gt;If the build is for a branch that just had it's history rewritten (for example, due to a rebase and force push), the starting commit may not actually be accessible in the repo anymore.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TRAVIS_COMMIT_RANGE&lt;/code&gt; only lists the range of commits for the most recent push. This means that if a user incrementally updates a PR by pushing to the branch it's based on, the above won't give you the full list of files for the PR, just the most recent push.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The third is actually the easiest to solve, so we'll cover that first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Looking at the whole PR
&lt;/h3&gt;

&lt;p&gt;In the case of a PR build, the environment variable &lt;code&gt;TRAVIS_PULL_REQUEST&lt;/code&gt; will be set equal to the PR number on GitHub. For other builds, it's set to the exact string &lt;code&gt;false&lt;/code&gt;. Thus, checking if a build is a PR or not is as easy as checking if &lt;code&gt;TRAVIS_PULL_REQUEST&lt;/code&gt; is &lt;em&gt;not&lt;/em&gt; equal to the string &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once we know we're dealing with a PR build, there are two additional facts we can rely on to determine what commits are part of the PR:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The currently checked out revision (&lt;code&gt;HEAD&lt;/code&gt; in &lt;code&gt;git&lt;/code&gt; terms) will be the most recent commit in the PR.&lt;/li&gt;
&lt;li&gt;The the name of the branch the PR is against (that is, the one it will be merged into) can be found in the &lt;code&gt;TRAVIS_BRANCH&lt;/code&gt; environment variable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Based on this, we can update the above code like so to inspect the entirety of a PR when dealing with a PR build:&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="nv"&gt;CHANGED_FILES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TRAVIS_PULL_REQUEST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"false"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# This isn't a PR build.&lt;/span&gt;
    &lt;span class="nv"&gt;COMMIT_RANGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TRAVIS_COMMIT_RANGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'.'&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 1,4 &lt;span class="nt"&gt;--output-delimiter&lt;/span&gt; &lt;span class="s1"&gt;'..'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nv"&gt;CHANGED_FILES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMMIT_RANGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="c"&gt;# This is a PR build.&lt;/span&gt;
    &lt;span class="nv"&gt;CHANGED_FILES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TRAVIS_BRANCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;..HEAD &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Challenging Cases
&lt;/h3&gt;

&lt;p&gt;The other two issues are a bit more complicated. Both of them also functionally require looking at the 'whole' branch instead of the most recent commits, but unlike a PR we don't have a well defined base branch to check against, and on top of that we don't know exactly where the branch diverged.&lt;/p&gt;

&lt;p&gt;However, detecting these cases is actually pretty easy.&lt;/p&gt;

&lt;p&gt;As mentioned above, if the build is for a new branch (one Travis has never seen before), &lt;code&gt;TRAVIS_COMMIT_RANGE&lt;/code&gt; will be empty. That's trivial to check with a simple &lt;code&gt;[ -z "${TRAVIS_COMMIT_RANGE}" ]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The case of a history rewrite is a bit trickier, because we need to figure out if &lt;code&gt;git&lt;/code&gt; recognizes both the starting and ending commit as actual commits. Luckily, this can be done with a single &lt;code&gt;git&lt;/code&gt; command: &lt;code&gt;git cat-file -t $foo&lt;/code&gt;, where &lt;code&gt;$foo&lt;/code&gt; is the commit hash we want to check. If the hash represents an actual commit, that command will return the exact string 'commit', which we can also easy check for.&lt;/p&gt;

&lt;p&gt;Putting that all together, we get:&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="nv"&gt;CHANGED_FILES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TRAVIS_COMMIT_RANGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# This is a new branch.&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="c"&gt;# This isn't a new branch.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TRAVIS_PULL_REQUEST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"false"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="c"&gt;# This isn't a PR build.&lt;/span&gt;

        &lt;span class="c"&gt;# We need the individual commits to detect force pushes.&lt;/span&gt;
        &lt;span class="nv"&gt;COMMIT1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TRAVIS_COMMIT_RANGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 1 &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nv"&gt;COMMIT2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TRAVIS_COMMIT_RANGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 4 &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git cat-file &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMMIT1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; commit &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git cat-file &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMMIT2&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; commit &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
            &lt;span class="c"&gt;# This was a history rewrite.&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="c"&gt;# This is a 'normal' build.&lt;/span&gt;
            &lt;span class="nv"&gt;CHANGED_FILES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;COMMIT_RANGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;fi
    else&lt;/span&gt;
        &lt;span class="c"&gt;# This is a PR build.&lt;/span&gt;
        &lt;span class="nv"&gt;CHANGED_FILES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TRAVIS_BRANCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;..HEAD &lt;span class="nt"&gt;--&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi
fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you've got the detection done, there are three approaches that can be taken here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Just assume that everything you might do in this job needs to be done and move on with things. This is the simplest and most reliable option overall. You can find an example of a full script doing this &lt;a href="https://github.com/netdata/netdata/blob/master/.travis/run-unit-tests.sh"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Assume a specific 'base' branch to compare against and use &lt;code&gt;git merge-base&lt;/code&gt; to find the 'starting' commit to work from. This has some reliability issues because &lt;code&gt;git merge-base&lt;/code&gt; can be unpredictable under certain circumstances, and won't account reliably for changes that happened in the base branch relative to your current branch.&lt;/li&gt;
&lt;li&gt;Assume a specific 'base' branch to compare against, and then use the same logic as was used for PR builds. This is more reliable and predictable than using &lt;code&gt;git merge-base&lt;/code&gt;, but it can still miss things on occasion.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which approach you take really depends on what you're trying to do conditionally. If it's just something like unit tests, then the first approach (just assume you need to do everything) is generally fine. However, if you're using this for conditional deployment, you're probably going to want one of the other two approaches so that you don't redeploy things you don't need to.&lt;/p&gt;

</description>
      <category>ci</category>
      <category>git</category>
      <category>travisci</category>
    </item>
    <item>
      <title>Any tips for someone just starting out learning Elixir?</title>
      <dc:creator>Austin S. Hemmelgarn</dc:creator>
      <pubDate>Fri, 06 Sep 2019 18:55:46 +0000</pubDate>
      <link>https://dev.to/ahferroin7/any-tips-for-someone-just-starting-out-learning-elixir-1lmd</link>
      <guid>https://dev.to/ahferroin7/any-tips-for-someone-just-starting-out-learning-elixir-1lmd</guid>
      <description>&lt;p&gt;I recently decided to start learning Elixir.  I've already got a project in mind (I'm going to be converting some existing tools I use regularly on my systems from Python (a language I already have solid experience with) to Elixir since the concurrency model should help them be significantly more efficient), and I've worked through the whole 'Getting Started' guide on the website without any significant issues.&lt;/p&gt;

&lt;p&gt;However, it's drastically different from most of what I'm used to (I'm coming from a mostly Python background, with some JS and SH experience), and I'm wondering if anybody might have some advice for people new to the language?  Any particular gotcha's to watch out for that aren't well documented?  Possibly things that need to be handled significantly differently in Elixir compared to other languages for efficiency reasons?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>elixir</category>
    </item>
    <item>
      <title>The HTML5 template element.</title>
      <dc:creator>Austin S. Hemmelgarn</dc:creator>
      <pubDate>Mon, 19 Aug 2019 02:00:02 +0000</pubDate>
      <link>https://dev.to/ahferroin7/the-html5-template-element-26b6</link>
      <guid>https://dev.to/ahferroin7/the-html5-template-element-26b6</guid>
      <description>&lt;p&gt;Templated content is huge on the web.  Most sites do at least some form of templating either on the server side or the client side, and it's essentially mandatory on the client side if you're writing a single-page application (and wish to retain your sanity).&lt;/p&gt;

&lt;p&gt;Outside of sites using Web Components though, any templating on the client side is almost always done using either a dedicated templating library, or an application framework that supports templating.&lt;/p&gt;

&lt;p&gt;There is, however, another way: The HTML5 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; element.&lt;/p&gt;

&lt;h2&gt;
  
  
  What exactly is the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; element?
&lt;/h2&gt;

&lt;p&gt;Put simply, it provides an easy way to define a reusable fragment of HTML that can be manipulated just like you would the contents of the document itself, but without the overhead of actually updating the DOM or having to compile and parse strings of HTML.&lt;/p&gt;

&lt;p&gt;Anything inside of a &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; tag gets parsed just like regular HTML, except:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It doesn't get rendered.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags inside of it don't get run.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags inside of it don't get evaluated.&lt;/li&gt;
&lt;li&gt;It doesn't load any external resources (so you won't see any requests for the contents of &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt; tags).&lt;/li&gt;
&lt;li&gt;It can be accessed as a &lt;code&gt;DocumentFragment&lt;/code&gt; instance via the special &lt;code&gt;content&lt;/code&gt; property of the &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; element.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That &lt;code&gt;content&lt;/code&gt; property is the powerful bit here.  &lt;code&gt;DocumentFragment&lt;/code&gt; instances provide an API for manipulating their contents that's largely the same as the global &lt;code&gt;document&lt;/code&gt; object, so you can manipulate them like their own separate document.  On top of that, inserting a &lt;code&gt;DocumentFragment&lt;/code&gt; instance into the DOM is really fast compared to manipulating an element's &lt;code&gt;innerHTML&lt;/code&gt; property (or using &lt;code&gt;insertAdjacentHTML()&lt;/code&gt;), because the DOM structure already exists in memory, so it just needs to get linked into the DOM tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what can you do with it?
&lt;/h2&gt;

&lt;p&gt;A simple example might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;'#closeTemplate'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"close"&lt;/span&gt; &lt;span class="na"&gt;data-dismiss=&lt;/span&gt;&lt;span class="s"&gt;'modal'&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Close"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;times;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should be pretty recognizable to anybody who's used Bootstrap 4 before.  It just defines a template for a close icon for a Bootstrap 4 modal.&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;closeTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#closeTemplate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;

&lt;span class="k"&gt;for&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;modalHeader&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.modal-header&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="nx"&gt;modalHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;closeTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloneNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&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 then takes that template, and inserts it's contents at the end of each element with the &lt;code&gt;modal-header&lt;/code&gt; class (right where it should be if you're using regular Bootstrap 4 modals).&lt;/p&gt;

&lt;p&gt;The most important thing here is the call to the &lt;code&gt;cloneNode&lt;/code&gt; method of the template content.  This method, when called with &lt;code&gt;true&lt;/code&gt; as an argument, creates a completely new &lt;code&gt;DocumentFragment&lt;/code&gt; instance that's an exact copy of the original (this sounds like an expensive operation, but it's actually not that bad, especially for a small template like this). This is important because a given &lt;code&gt;DocumentFragment&lt;/code&gt; can only exist in the DOM in one place, so subsequent calls to any method that would insert it into the DOM will fail (without raising an exception for some reason that I cannot fathom). By creating a copy on each loop iteration, and inserting that into the DOM instead of the original, we avoid this issue.&lt;/p&gt;

&lt;h2&gt;
  
  
  OK, but what about using it for actual templates?
&lt;/h2&gt;

&lt;p&gt;Of course, most templating isn't as trivially simple as that.  In real life, you usually have to put something into the template for it to be useful, and you usually need to make sure certain attributes are set correctly before it gets put in the DOM.&lt;/p&gt;

&lt;p&gt;This is where the &lt;code&gt;DocumentFragment&lt;/code&gt; class having an interface almost the same as the global &lt;code&gt;document&lt;/code&gt; object comes in. With this, you can call &lt;code&gt;querySelector&lt;/code&gt; on the fragment just like you would to find elements in the page, and get a real &lt;code&gt;Element&lt;/code&gt; or &lt;code&gt;NodeList&lt;/code&gt; back that you can then manipulate just like if you had requested elements in the page itself.&lt;/p&gt;

&lt;p&gt;Consider the following example code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;'modalTemplate'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'modal fade'&lt;/span&gt; &lt;span class="na"&gt;tabindex=&lt;/span&gt;&lt;span class="s"&gt;'-1'&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;'dialog'&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;'true'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'modal-dialog'&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;'document'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'modal-content'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'modal-header'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;h5&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'modal-title'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/h5&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;'button'&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'close'&lt;/span&gt; &lt;span class="na"&gt;data-dismiss=&lt;/span&gt;&lt;span class="s"&gt;'modal'&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;'close'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;'true'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;times;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'modal-body'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'modal-footer'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;'button'&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'btn btn-secondary'&lt;/span&gt; &lt;span class="na"&gt;data-dismiss=&lt;/span&gt;&lt;span class="s"&gt;'modal'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Close&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;'button'&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'btn btn-primary'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Save&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now our template is a basic skeleton for a Bootstrap 4 modal.  Note that there are no ID attributes (or anything that references them) here.  Template contents still need to meet the uniqueness requirement for ID attributes across the whole page, so it's safest to just avoid them in the template itself and populate them from your code as you're using the template.&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;modalTemplate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#modalTemplate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;createModal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&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;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;modalTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloneNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&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;modal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.modal&lt;/span&gt;&lt;span class="dl"&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;modalTitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.modal-title&lt;/span&gt;&lt;span class="dl"&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;modalBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.modal-body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="nx"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-labelledby&lt;/span&gt;&lt;span class="dl"&gt;'&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;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;Title`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;modalTitle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&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;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;Title`&lt;/span&gt;
    &lt;span class="nx"&gt;modalTitle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;

    &lt;span class="nx"&gt;modalBody&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;

    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&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;id&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's a function to turn that template into an actual modal in the document.  This adds appropriate &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;aria-labelledby&lt;/code&gt; attributes to the root element of the modal, an appropriate &lt;code&gt;id&lt;/code&gt; and text contents to the title bar, and then adds whatever needs to be in the body of the modal before adding the modal itself to the end of the document's &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element.&lt;/p&gt;

&lt;h2&gt;
  
  
  But wait, there's more!
&lt;/h2&gt;

&lt;p&gt;Because you're doing all of this template construction from JavaScript, you also have the full power of JavaScript available for flow control logic.  You'll notice in the example above that we used JavaScript template strings to compute the correct values for the title's ID and the modal's &lt;code&gt;aria-labelledby&lt;/code&gt; attribute, but you're not even limited to that.  You can easily do complex flow-control using loops, conditionals, and even try/catch statements.&lt;/p&gt;

&lt;p&gt;On top of that, because &lt;code&gt;DocumentFragment&lt;/code&gt; works almost the same as the DOM, you can inject HTML5 templates into other HTML5 templates.  For example, the function above could easily be extended to instead accept a &lt;code&gt;DocumentFragment&lt;/code&gt; instance for the modal body, which could itself be created from another HTML template.&lt;/p&gt;

&lt;p&gt;By leveraging these tow facts, you can create complex layouts composed of multiple HTML5 templates with little effort.&lt;/p&gt;

&lt;p&gt;Additionally, the templates (including the associated JavaScript) tend to be smaller than the equivalent pre-compiled template code for a lot of JavaScript templating libraries (given my own experience, it's about 5-25% smaller than an equivalent pre-compiled lodash or underscore template), they render faster when inserted into the DOM, and you need no special tools to deal with building or checking them, because they're just plain HTML and plain JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  But surely it's not widely supported?
&lt;/h2&gt;

&lt;p&gt;The HTML5 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; element is actually very well supported.  Every major browser released in the last 3 years fully supports it, and most released in the past 5 years do too.  You can check the exact support data on &lt;a href="https://caniuse.com/#feat=template"&gt;Can I Use?&lt;/a&gt;.  As of writing this, it's just short of 95% market share for availability.&lt;/p&gt;

&lt;p&gt;The caveat here is that Internet Explorer lacks support (and Opera Mini and the BlackBerry browser, but neither of those has enough market share to matter in many cases).  However, there are a number of polyfills out there that will get you proper support in IE 11, and rudimentary support in IE 9 and IE 10, as well as smoothing over some of the issues with the older implementations (the Can I Use? link above includes links to a couple of good polyfills, and there's also one included with the Web Components project).&lt;/p&gt;

&lt;p&gt;Now, obviously, this is only really any good if you're not using a full application framework.  Most of them do the templating themselves because it's just easier, and thus don't do all that well when you mix them with other templating techniques.  On the other hand, if you don't need a full application framework for anything other than templating, this may be an easy alternative that would let you eliminate one of your dependencies (and probably speed up your app too).&lt;/p&gt;

</description>
      <category>html</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
