<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Buzzword Engineering]]></title><description><![CDATA[Buzzword Engineering]]></description><link>https://buzzword.engineering</link><generator>GatsbyJS</generator><lastBuildDate>Wed, 01 Dec 2021 19:42:48 GMT</lastBuildDate><item><title><![CDATA[The buzzword.engineering Tech Stack]]></title><description><![CDATA[I finally got around to putting a blog stack together that uses Obsidian, Gatsby, and automates tweeting out new posts with Pipedream.]]></description><link>https://buzzword.engineering/post/blog-tech-stack</link><guid isPermaLink="false">https://buzzword.engineering/post/blog-tech-stack</guid><pubDate>Sat, 26 Dec 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;welcome&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#welcome&quot; aria-label=&quot;welcome permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Welcome!&lt;/h1&gt;
&lt;p&gt;I&apos;ve been meaning to get around to setting up a blog for a long time. In the past, I&apos;ve gotten as far as getting halfway through trying out different static site generators before getting depressed about my lack of frontend design chops and given up. &lt;/p&gt;
&lt;p&gt;The perfect storm finally came: &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I took &lt;strong&gt;two weeks off&lt;/strong&gt;. After recharging my batteries for a few days, I was ready for a little side project. &lt;/li&gt;
&lt;li&gt;I recently discovered &lt;a href=&quot;https://obsidian.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Obsidian&lt;/a&gt;, which is a dope AF note-taking app. &lt;/li&gt;
&lt;li&gt;I&apos;ve tried a decent number of static site generators to build documentation for various projects and wanted to take a deeper dive into &lt;a href=&quot;https://gatsbyjs.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Gatsby&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;I recently discovered &lt;a href=&quot;https://pipedream.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Pipedream&lt;/a&gt; and wanted to use it for something. &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I wanted to see if i could use Obsidian as a &lt;a href=&quot;https://en.wikipedia.org/wiki/Content_management_system&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;content management system (CMS)&lt;/a&gt; for a tech blog and Pipedream to automate tweeting out new blog posts. &lt;/p&gt;
&lt;div class=&quot;admonition admonition-caution alert alert--warning&quot;&gt;&lt;div class=&quot;admonition-heading&quot;&gt;&lt;h5&gt;&lt;span class=&quot;admonition-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Spoiler Alert&lt;/strong&gt;&lt;/h5&gt;&lt;/div&gt;&lt;div class=&quot;admonition-content&quot;&gt;&lt;p&gt;It was, in fact, possible.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Anyway, here&apos;s Buzzword Engineering&apos;s inaugural blog post. If you like it, go give me a github star on the &lt;a href=&quot;https://github.com/steven-terrana/steven-terrana.github.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;blog repo&lt;/a&gt; or something. It&apos;s a nice dopamine boost and fuels my self-worth. &lt;/p&gt;
&lt;h1 id=&quot;tech-stack&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#tech-stack&quot; aria-label=&quot;tech stack permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Tech Stack&lt;/h1&gt;
&lt;p&gt;Let&apos;s dive in. Here&apos;s a digram for those visual learners out there. &lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f0e7c507dc593d346366fe97b0306a5a/45662/overview.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 33.33333333333333%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsSAAALEgHS3X78AAABMklEQVQoz41S2ZKtIAz0/z9xLJ2jHHcBF0DBpSfhlj7fVKUKTafTHUiO40RZfGCNwRP3fb/JobVGU9f4n0jctsEscyQ0lN77t3gcB4y1aJsGoiywrCssfYfgEQjH2GEY0Pc9nHPYiCtp2xZd18WsScU0TS+h94GAG4qiQJqmcWhZlugJ68wKIQTyPEeWZaiqKjpJPgTuviWcNViW5VXIdvXYU3OLSpSoRYHf9Ady6GP9PM9IoOWIWUlM8xzVJw3Z4YmslOVrpWJuq4El4Err4BorYKWKaiGEOPi6LsyThhqHOGTf93+W8zyjpg5SyrjHJzYik0Qgvl8i+0RFjGFCbl5pp9zHq+IzD0sushacwbk73IfHoiUk2VR9SypnqlkYNcLSf09EfFHPStj28xr4zIr/AIdVGN6XvtCPAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/f0e7c507dc593d346366fe97b0306a5a/8ac56/overview.webp 240w,
/static/f0e7c507dc593d346366fe97b0306a5a/d3be9/overview.webp 480w,
/static/f0e7c507dc593d346366fe97b0306a5a/e46b2/overview.webp 960w,
/static/f0e7c507dc593d346366fe97b0306a5a/e97dc/overview.webp 1410w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/f0e7c507dc593d346366fe97b0306a5a/8ff5a/overview.png 240w,
/static/f0e7c507dc593d346366fe97b0306a5a/e85cb/overview.png 480w,
/static/f0e7c507dc593d346366fe97b0306a5a/d9199/overview.png 960w,
/static/f0e7c507dc593d346366fe97b0306a5a/45662/overview.png 1410w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/f0e7c507dc593d346366fe97b0306a5a/d9199/overview.png&quot;
          alt=&quot;This diagram shows an overview of buzzword.engineering tech stack and associated automation&quot;
          title=&quot;This diagram shows an overview of buzzword.engineering tech stack and associated automation&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;admonition admonition-tip alert alert--success&quot;&gt;&lt;div class=&quot;admonition-heading&quot;&gt;&lt;h5&gt;&lt;span class=&quot;admonition-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;12&quot; height=&quot;16&quot; viewBox=&quot;0 0 12 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Excalidraw is Dope&lt;/strong&gt;&lt;/h5&gt;&lt;/div&gt;&lt;div class=&quot;admonition-content&quot;&gt;&lt;p&gt;If you haven&apos;t heard of it, stop reading this and go play with &lt;a href=&quot;https://excalidraw.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Excalidraw&lt;/a&gt; and then come back. It&apos;s the tool I used to sketch out this diagram.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;obsidian&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#obsidian&quot; aria-label=&quot;obsidian permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Obsidian&lt;/h2&gt;
&lt;p&gt;I&apos;ll keep this short.  Maybe a future blog post will talk about Obsidian in a lot more detail. For now, let me just say that I&apos;ve tried to get into taking notes for... a long time. I could never do it in college. I struggle to do it for work. I&apos;ve always found that taking notes takes away from my ability to absorb the content in the moment and make meaningful contributions. &lt;/p&gt;
&lt;p&gt;Obsidian was the first app that actually made me &lt;strong&gt;want&lt;/strong&gt; to take notes. The general idea is that all your notes are written in markdown. Jumping around is super easy with &lt;code class=&quot;language-text&quot;&gt;CMD + O&lt;/code&gt; (which also will create pages for you if they don&apos;t exist).  Linking between pages to build connections is really easy as can be with a syntax like &lt;code class=&quot;language-text&quot;&gt;[[this]]&lt;/code&gt;. Obsidian builds a visual graph of the relationships between pages (I&apos;m a sucker for graphs). And finally, you can build templates and insert them with &lt;code class=&quot;language-text&quot;&gt;CMD + T&lt;/code&gt;. Templates dramatically simplified the boiler plate needed to capture who&apos;s attending a meeting, agenda, the date, etc. &lt;/p&gt;
&lt;p&gt;Long story short, try it out.  (Or don&apos;t, whatever.)  I&apos;m a fan and thought that maybe if I can use it as the interface for writing blog posts that I might &lt;em&gt;actually&lt;/em&gt; write some. &lt;/p&gt;
&lt;h3 id=&quot;automated-backups&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#automated-backups&quot; aria-label=&quot;automated backups permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Automated Backups&lt;/h3&gt;
&lt;p&gt;Obsidian has some 3rd-party plugins that do nifty things.  One of these plugins is called &lt;a href=&quot;https://github.com/denolehov/obsidian-git&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Obsidian Git&lt;/a&gt; which can automatically backup your notes to a Git repository.&lt;/p&gt;
&lt;p&gt;I figured that had to be a way to fetch markdown content from a remote github repository and use it as a content source for Gatsby. There was.&lt;/p&gt;
&lt;h3 id=&quot;defining-post-information&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#defining-post-information&quot; aria-label=&quot;defining post information permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Defining Post Information&lt;/h3&gt;
&lt;p&gt;Blog post information is defined through the markdown frontmatter.  For example, the frontmatter for this blog post: &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; The buzzword.engineering Tech Stack
&lt;span class=&quot;token key atrule&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;12/26/2020&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;post&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;slug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; blog&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tech&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;stack
&lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;I finally got around to putting a blog together that uses Obsidian, Gatsby, and automates tweeting out new posts with Pipedream.&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;gatsby&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#gatsby&quot; aria-label=&quot;gatsby permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Gatsby&lt;/h2&gt;
&lt;p&gt;I think it&apos;s important to start here by saying that I&apos;m &lt;strong&gt;not&lt;/strong&gt; a frontend developer. Well, let&apos;s rephrase that. I&apos;m writing a blog post that has Gatsby in it.  So it&apos;s probably more accurate to say that I&apos;m a &lt;em&gt;very&lt;/em&gt; junior frontend developer. &lt;/p&gt;
&lt;p&gt;My mental model for Gatsby so far is that it&apos;s a framework for building static site generators. There might be a couple frontend purists or gatsby enthusiasts out there who take issue with that definition, please let me know if you&apos;ve got a better one down in the comments. &lt;/p&gt;
&lt;p&gt;There are two main components of Gatsby that drew me to it: &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It uses &lt;a href=&quot;https://reactjs.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;React&lt;/a&gt;, which is a lot more powerful to me over something like &lt;a href=&quot;https://handlebarsjs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;handlebars&lt;/a&gt; or go-based html templating. &lt;/li&gt;
&lt;li&gt;Gatsby is extensible with a rich plugin ecosystem that contribute to a shared &lt;a href=&quot;https://graphql.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;GraphQL&lt;/a&gt; data layer. When developing your site, you can query the data layer to fetch content for particular pages/components.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I like React and I think Gatsby&apos;s extensibility framework and GraphQL data layer is &lt;strong&gt;brilliant&lt;/strong&gt;. &lt;/p&gt;
&lt;h3 id=&quot;the-starter&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-starter&quot; aria-label=&quot;the starter permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Starter&lt;/h3&gt;
&lt;p&gt;Another great thing about Gatsby is their concept of Starters. For this blog, I kicked things off with the &lt;a href=&quot;https://github.com/alxshelepenok/gatsby-starter-lumen&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;gatsby-starter-lumen&lt;/a&gt;. &lt;/p&gt;
&lt;h3 id=&quot;fetching-content&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#fetching-content&quot; aria-label=&quot;fetching content permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Fetching Content&lt;/h3&gt;
&lt;p&gt;The first thing I had to customize was content sources. The Lumen starter fetches content from the same repository as the blog itself. Thankfully, there&apos;s a Gatsby plugin called &lt;a href=&quot;https://www.gatsbyjs.com/plugins/gatsby-source-git&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;gatsby-source-git&lt;/code&gt;&lt;/a&gt; that allows you to fetch content from a remote Git repository. &lt;/p&gt;
&lt;p&gt;During development, I wanted to be able to fetch content from the local copy of the Obsidian backup repository. Gatsby plugins are done by exporting a javascript object from a file called &lt;code class=&quot;language-text&quot;&gt;gatsby-config.js&lt;/code&gt;.  &lt;/p&gt;
&lt;p&gt;Here, I toggle between using the &lt;code class=&quot;language-text&quot;&gt;gatsby-source-git&lt;/code&gt; plugin and the [&lt;code class=&quot;language-text&quot;&gt;gatsby-source-filesystem&lt;/code&gt;] based on whether a &lt;code class=&quot;language-text&quot;&gt;GATSBY_PREVIEW&lt;/code&gt; environment variable is set. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;js&quot;&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;GATSBY_PREVIEW&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;using local vault path: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;siteConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;obsidian&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vaultPath&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;plugins&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unshift&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    resolve&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;gatsby-source-filesystem&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    options&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      path&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; siteConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;obsidian&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vaultPath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;local_obsidian&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      ignore&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;**/.git/**/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;**/.obsidian/**/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;**/Templates/**/*&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fetching from remote repo: &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; siteConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;obsidian&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;repo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;plugins&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unshift&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    resolve&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;gatsby-source-git&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    options&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;obsidian&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      remote&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; siteConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;obsidian&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;repo&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      patterns&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;!**/Templates/**/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;**/*&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;comments&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#comments&quot; aria-label=&quot;comments permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Comments&lt;/h3&gt;
&lt;p&gt;Comments on blog posts are made possible through a nifty tool called &lt;a href=&quot;https://utteranc.es/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;utteranc.es&lt;/a&gt;. It&apos;s a GitHub Application that uses GitHub Issue threads per blog post to track comments. &lt;/p&gt;
&lt;h3 id=&quot;post-filtering&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#post-filtering&quot; aria-label=&quot;post filtering permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Post Filtering&lt;/h3&gt;
&lt;p&gt;In the spirit of premature optimization, I wanted to integrate a way to filter blog posts with fuzzy-searching. To accomplish this, I integrated &lt;a href=&quot;https://fusejs.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Fuse.js&lt;/a&gt; and added a new &lt;code class=&quot;language-text&quot;&gt;Filter&lt;/code&gt; component to the blog. &lt;/p&gt;
&lt;p&gt;Most of the logic for how this was accomplished can be seen in the &lt;a href=&quot;https://github.com/steven-terrana/steven-terrana.github.io/blob/main/src/templates/index-template.js&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Index Template&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;admonition admonition-caution alert alert--warning&quot;&gt;&lt;div class=&quot;admonition-heading&quot;&gt;&lt;h5&gt;&lt;span class=&quot;admonition-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;caution&lt;/h5&gt;&lt;/div&gt;&lt;div class=&quot;admonition-content&quot;&gt;&lt;p&gt;I wanted to insert a gif of the filtering taking place. Apparently that&apos;s easier said than done with Gatsby and&lt;code class=&quot;language-text&quot;&gt;gatsby-transform-remark&lt;/code&gt;.  I&apos;ll update this post once I get gifs working 🙄.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;automation&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#automation&quot; aria-label=&quot;automation permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Automation&lt;/h2&gt;
&lt;p&gt;With the site actually working how I wanted it to, I got to focus on the side of things I&apos;m actually good at: digital duct tape. The goal is for changes in markdown content in the Obsidian backup repository to trigger a deployment of the site and if there is a new blog post, to send out a tweet letting you all know about it. &lt;/p&gt;
&lt;h3 id=&quot;step-1-github-action-on-the-obsidian-backup-repo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-github-action-on-the-obsidian-backup-repo&quot; aria-label=&quot;step 1 github action on the obsidian backup repo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 1: GitHub Action on the Obsidian Backup Repo&lt;/h3&gt;
&lt;p&gt;First things first, the content repository needs to trigger a deployment of the site. The easiest way I could think to accomplish this would be to a GitHub Action on the blog post repository that does the build/deploy logic. &lt;/p&gt;
&lt;p&gt;This meant that I needed a way to invoke a GitHub Action on one repository as part of the execution of an Action on another repository. This is where the &lt;a href=&quot;https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#repository_dispatch&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;repository_dispatch&lt;/code&gt;&lt;/a&gt; event comes in handy. Basically, it means that you can use the GitHub API to trigger an Action. &lt;/p&gt;
&lt;p&gt;Here&apos;s what the GitHub Action workflow looks like for the obsidian repository: &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Trigger Build
&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# trigger this action on changes to the main branch&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; main &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# Allows you to run this workflow manually from the Actions tab&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;trigger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Trigger Upstream Blog Action
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          curl -XPOST \
          -u &quot;${{ secrets.PAT_USERNAME}}:${{secrets.PAT_TOKEN}}&quot; \
          -H &quot;Accept: application/vnd.github.everest-preview+json&quot; \
          -H &quot;Content-Type: application/json&quot; \
          https://api.github.com/repos/steven-terrana/steven-terrana.github.io/dispatches \
          --data &apos;{&quot;event_type&quot;: &quot;blog&quot;}&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;step-2-github-action-on-the-blog-repo&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-2-github-action-on-the-blog-repo&quot; aria-label=&quot;step 2 github action on the blog repo permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 2: GitHub Action on the Blog Repo&lt;/h3&gt;
&lt;p&gt;Sweet. Now commits to the Obsidian backup repository will trigger actions on the blog repository. &lt;/p&gt;
&lt;p&gt;The next step was to automate the build and deployment steps using a GitHub Action on the blog repository. Here&apos;s what that action looks like: &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Build and Publish
&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;repository_dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;build-deploy-notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ubuntu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;latest
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Checkout Code 🛎
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v2
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 
          &lt;span class=&quot;token key atrule&quot;&gt;persist-credentials&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;
       &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Install &amp;amp; Build 🔧
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          npm ci
          npm run build
          echo &quot;buzzword.engineering&quot; &gt; public/CNAME&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 
          &lt;span class=&quot;token key atrule&quot;&gt;PAT_USER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.PAT_USER &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;PAT_TOKEN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.PAT_TOKEN &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; peaceiris/actions&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;gh&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;pages@v3
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;github_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; secrets.GITHUB_TOKEN &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;publish_dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; public
          &lt;span class=&quot;token key atrule&quot;&gt;force_orphan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;  &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This blog is hosted using GitHub Pages, so you&apos;ll notice a few things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I add a custom &lt;code class=&quot;language-text&quot;&gt;CNAME&lt;/code&gt; file to the &lt;code class=&quot;language-text&quot;&gt;public&lt;/code&gt; directory so that GitHub Pages knows the custom domain for this blog.  (I should definitely incorporate this into an inherit part of the build of the site using the &lt;code class=&quot;language-text&quot;&gt;onPostBuild&lt;/code&gt; Gatsby Node API method or something). &lt;/li&gt;
&lt;li&gt;I use the &lt;code class=&quot;language-text&quot;&gt;peaceiris/actions-gh-pages&lt;/code&gt; action to publish the site. &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All in all, this was a pretty painless setup. &lt;/p&gt;
&lt;h3 id=&quot;step-3-automating-tweets&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-3-automating-tweets&quot; aria-label=&quot;step 3 automating tweets permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 3: Automating Tweets&lt;/h3&gt;
&lt;p&gt;So at this point, we&apos;ve got content changes automatically getting deployed to GitHub Pages. The whole process takes about &lt;strong&gt;three minutes&lt;/strong&gt; from commit to publish. &lt;/p&gt;
&lt;p&gt;The last piece was to automate letting all of you know about the whatever new insightful thing I had to say! &lt;/p&gt;
&lt;p&gt;I had stumbled on &lt;a href=&quot;https://pipedream.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Pipedream&lt;/a&gt; before through targeted ads and sort of ignored it until I saw &lt;a href=&quot;https://twitter.com/rawkode&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;David McKay&lt;/a&gt; talk about how much he loves it on &lt;a href=&quot;https://rawkode.live&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;rawkode.live&lt;/a&gt;. Here&apos;s a &lt;a href=&quot;https://youtu.be/Q8ZJ_5zxfmo&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;link to the stream&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;I went into this adventure thinking I was going to have to do all kinds of fancy logic and scripting to make this possible. I was wrong. &lt;/p&gt;
&lt;p&gt;After setting up a Pipedream account, starting looking at what event sources were available to trigger a workflow. Well, the Lumen gatsby starter had already integrated an &lt;a href=&quot;https://buzzword.engineering/rss.xml&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;RSS feed&lt;/a&gt; using the &lt;a href=&quot;https://www.gatsbyjs.com/plugins/gatsby-plugin-feed&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;gatsby-plugin-feed&lt;/code&gt;&lt;/a&gt; plugin. Pipedream had an event source for monitoring RSS feeds. Done. &lt;/p&gt;
&lt;p&gt;That was easy enough. Next up was automating how to send the tweet. I figured that would mean doing some research on a Twitter Rest API or SDK. &lt;/p&gt;
&lt;p&gt;Nope!  Pipedream also had a step for automating the sending off a tweet and even made it really simple to query the event that triggered the workflow to template the tweet content with the new blog post information. Here&apos;s what this whole setup looks like in Pipedream:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/380f5285cb42aa0d885f591d6f8c3c64/7575b/pipedream.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 42.083333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAYAAAD5nd/tAAAACXBIWXMAAAsSAAALEgHS3X78AAAA6ElEQVQoz52R3W7DIAyFef+33FRpk7IkUH6MDdGZTVO1YXe7+MSxrWMwdtv3DR+fN3jv0Vob9N4vHMfxJ/eOiICYkZngQsz4WnasPuKeslJQKisyTmJBKoRMdehX7aUtP2rEcKsP2PYd95gQc3k0Vf0k5oxl3bBuO+JUmwmK20McBtLbqt7C0iCtX2j9GMz5mapeZ53tlcTtHFdGwWKSE57iGa2Zx7yu6EghaEMipY6l2BL+g6jX1VqxLD9I+j+t95PjQj+ZdZu0eR3ruoOOukWGzwKfdP1kz2/I9XE+sdjGKzzFp7b//wWFzHB83aCQkgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/380f5285cb42aa0d885f591d6f8c3c64/8ac56/pipedream.webp 240w,
/static/380f5285cb42aa0d885f591d6f8c3c64/d3be9/pipedream.webp 480w,
/static/380f5285cb42aa0d885f591d6f8c3c64/e46b2/pipedream.webp 960w,
/static/380f5285cb42aa0d885f591d6f8c3c64/f992d/pipedream.webp 1440w,
/static/380f5285cb42aa0d885f591d6f8c3c64/40fa0/pipedream.webp 1608w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/380f5285cb42aa0d885f591d6f8c3c64/8ff5a/pipedream.png 240w,
/static/380f5285cb42aa0d885f591d6f8c3c64/e85cb/pipedream.png 480w,
/static/380f5285cb42aa0d885f591d6f8c3c64/d9199/pipedream.png 960w,
/static/380f5285cb42aa0d885f591d6f8c3c64/07a9c/pipedream.png 1440w,
/static/380f5285cb42aa0d885f591d6f8c3c64/7575b/pipedream.png 1608w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/380f5285cb42aa0d885f591d6f8c3c64/d9199/pipedream.png&quot;
          alt=&quot;Screenshot of the Pipedream configuration for sending a tweet based on an updated RSS feed&quot;
          title=&quot;Screenshot of the Pipedream configuration for sending a tweet based on an updated RSS feed&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;This whole workflow took about 1 minute to figure out and configure. I was almost disappointed in how easy it was. In the end, it was pretty boring. And that&apos;s probably the best compliment I can give Pipedream. &lt;/p&gt;</content:encoded></item><item><title><![CDATA[A Groovy Alternative to Ordinals With Annotation-Driven Directed Acyclic Graphs]]></title><description><![CDATA[Using JGraphT to build an annotation-driven approach to directed acyclic graph dependency resolution as an alternative to ordinals within the Jenkins Templating Engine.]]></description><link>https://buzzword.engineering/post/dag-alternative-to-ordinals-jte</link><guid isPermaLink="false">https://buzzword.engineering/post/dag-alternative-to-ordinals-jte</guid><pubDate>Sat, 02 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;h1 id=&quot;some-background-context&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#some-background-context&quot; aria-label=&quot;some background context permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Some Background Context&lt;/h1&gt;
&lt;p&gt;If you know anything about me, it&apos;s probably that I&apos;m the lead maintainer of the &lt;a href=&quot;https://github.com/jenkinsci/templating-engine-plugin&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Jenkins Templating Engine (JTE)&lt;/a&gt;. While working on JTE 2.0, we refactored how pipeline initialization works. &lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/621b804243b8468ca3c14756238a0b6b/fe9e8/jte-initialization.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.333333333333336%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsSAAALEgHS3X78AAABuElEQVQoz3VTi26DMAzk//9rmlS1U7eqGi2iUFroILzCM7x6s1Nl2rTN0mEngdh3NtY4juj7HuwZXddhnmcURYGJ/DQvXxh5vTziYZoxU8x2v99hzGqaBuv1GqvVCpvNBtvtVl8qkgSprOCIDNdCavhZAVG3OCYpntwAUVn9vpAfC2UyVZrDPM9/vPjdeL/rlfYGZt/CPybLEmXXI6Iqs6ZD2rTIWvKEdhgxT9OPBAYWV2bbtsbhcNCe96IwRFrVCIjq+03A/hBw4xTHa4RX74xaSs2Idf5uFtMVQsD3fYR0iaF6vV6Q1Q2CXOIliLAm7NwTXh0Xz7aDIs8ejRyUjn3Pg6Qk/1Ku6JCpuSKHk2TY3xKNt/ADF6pa68ySUOyEN5zCiJrZwmrbFgymyd3leBgGXelIOkkSX5EfiFo/mvWD5o0k8VJKSHKcYgGlFKzz+axHxXVd7HY77Pd7lNQQ53hExYnoMq60VoPGQnIkpcRAHy+URFEhlyDQ73tEW2vIQ8yVsa+qx2wVVGFHlXKnucs8fwlp2pFugi5Uqoeic2bzaw7/sjiOdQeZJv8d/JcwFK3nP+bTjM0nX0NNydFCEsMAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/621b804243b8468ca3c14756238a0b6b/8ac56/jte-initialization.webp 240w,
/static/621b804243b8468ca3c14756238a0b6b/d3be9/jte-initialization.webp 480w,
/static/621b804243b8468ca3c14756238a0b6b/e46b2/jte-initialization.webp 960w,
/static/621b804243b8468ca3c14756238a0b6b/b28d4/jte-initialization.webp 1146w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/621b804243b8468ca3c14756238a0b6b/8ff5a/jte-initialization.png 240w,
/static/621b804243b8468ca3c14756238a0b6b/e85cb/jte-initialization.png 480w,
/static/621b804243b8468ca3c14756238a0b6b/d9199/jte-initialization.png 960w,
/static/621b804243b8468ca3c14756238a0b6b/fe9e8/jte-initialization.png 1146w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/621b804243b8468ca3c14756238a0b6b/d9199/jte-initialization.png&quot;
          alt=&quot;Overview of the JTE initialization process&quot;
          title=&quot;Overview of the JTE initialization process&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Aggregated Pipeline Configurations&lt;/strong&gt;: The Jenkins folder structure is used to create a configuration hierarchy. Pipeline configurations are aggregated across the hierarchy into an Aggregated Pipeline Configuration. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Interpret Pipeline Configuration&lt;/strong&gt;: JTE has what are called &lt;em&gt;Pipeline Primitives&lt;/em&gt;.  They&apos;re just objects that get created by parsing the pipeline configuration and instantiating objects defined to be stored in a common Groovy binding.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Determine and Execute the Pipeline Template&lt;/strong&gt;: The configured pipeline template is executed just like any other Jenkinsfile, just with the common binding that&apos;s been injected with primitives so that the templated aspects actually &lt;em&gt;do&lt;/em&gt; something. &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The benefit of this design is that all 3 phases of initialization are totally decoupled. The pipeline configuration files are a custom configuration DSL that get compiled into a &lt;code class=&quot;language-text&quot;&gt;PipelineConfigurationObject&lt;/code&gt;.  Each Pipeline Primitive has a corresponding &lt;code class=&quot;language-text&quot;&gt;TemplatePrimitiveInjector&lt;/code&gt; that implements an extension point.  We then dynamically fetch all the different Injectors and iterate over them 3 times. The first time is to invoke an optional interface on each injector to validate the pipeline configuration. The second time is to invoke an optional interface on each injector to instantiate a primitive and store it in the binding. The third time is to validate the completed binding.&lt;/p&gt;
&lt;p&gt;This all happens from a common entry point that has no idea which Injectors exist. This makes it possible for 3rd party plugins to extend JTE&apos;s functionality through adding custom Injectors. &lt;/p&gt;
&lt;p&gt;The challenge is now: How can certain Injectors ensure that specific other injectors run before them? &lt;/p&gt;
&lt;p&gt;Here&apos;s an example required iteration order: &lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 960px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/92241cc88b0718d0ab3afd24ccd1049e/04784/example-iteration-order.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 36.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsSAAALEgHS3X78AAAAz0lEQVQoz6VR2QrDMAzL//9gKb1oS+8D2tL70JDBIw8bDGYQSowVy455ngeM67qwbRv2fRde11V4nmfEcYwoigRBEMB1XdR1LTrqbRgm7/uWB8jawA42O8/zzcdxSO3XB+mExSyiYBgGtG2LpmnQ9/1b/EuYLMvQdR3GcRSXZI7meR4cx5ERl2WRJmEYyl25KArRpGkK3/clb2h/mibZFc/kqqpQliXyPBemSHfLGp4JTkP3ds6oVU3+G0aXaX8OwTHplqDLJElkFZzm02coXhCpH4gAdnHNAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;picture&gt;
        &lt;source
          srcset=&quot;/static/92241cc88b0718d0ab3afd24ccd1049e/8ac56/example-iteration-order.webp 240w,
/static/92241cc88b0718d0ab3afd24ccd1049e/d3be9/example-iteration-order.webp 480w,
/static/92241cc88b0718d0ab3afd24ccd1049e/e46b2/example-iteration-order.webp 960w,
/static/92241cc88b0718d0ab3afd24ccd1049e/963af/example-iteration-order.webp 1174w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/webp&quot;
        /&gt;
        &lt;source
          srcset=&quot;/static/92241cc88b0718d0ab3afd24ccd1049e/8ff5a/example-iteration-order.png 240w,
/static/92241cc88b0718d0ab3afd24ccd1049e/e85cb/example-iteration-order.png 480w,
/static/92241cc88b0718d0ab3afd24ccd1049e/d9199/example-iteration-order.png 960w,
/static/92241cc88b0718d0ab3afd24ccd1049e/04784/example-iteration-order.png 1174w&quot;
          sizes=&quot;(max-width: 960px) 100vw, 960px&quot;
          type=&quot;image/png&quot;
        /&gt;
        &lt;img
          class=&quot;gatsby-resp-image-image&quot;
          src=&quot;/static/92241cc88b0718d0ab3afd24ccd1049e/d9199/example-iteration-order.png&quot;
          alt=&quot;example iteration order&quot;
          title=&quot;example iteration order&quot;
          loading=&quot;lazy&quot;
          style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        /&gt;
      &lt;/picture&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;div class=&quot;admonition admonition-tip alert alert--success&quot;&gt;&lt;div class=&quot;admonition-heading&quot;&gt;&lt;h5&gt;&lt;span class=&quot;admonition-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;12&quot; height=&quot;16&quot; viewBox=&quot;0 0 12 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;Learn More About JTE&lt;/h5&gt;&lt;/div&gt;&lt;div class=&quot;admonition-content&quot;&gt;&lt;p&gt;To learn more about the Jenkins Templating Engine, I&apos;d recommend checking out this &lt;a href=&quot;https://www.youtube.com/watch?v=pz_kPpb9C1w&amp;#x26;feature=youtu.be&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Jenkins Online Meetup&lt;/a&gt; and the &lt;a href=&quot;https://boozallen.github.io/sdp-docs/jte/2/index.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;docs&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h1 id=&quot;the-common-approach-ordinals&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#the-common-approach-ordinals&quot; aria-label=&quot;the common approach ordinals permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;The Common Approach: Ordinals&lt;/h1&gt;
&lt;p&gt;Typically, the way I&apos;ve seen this problem solved is through a pattern called Ordinals. Ordinals are defined, mathematically, as an integer or character indicating a position in a sequence. &lt;/p&gt;
&lt;p&gt;When this pattern is implemented, an annotation is defined and optionally applied to elements in the sequence. Prior to iterating, elements are sorted according to their self-defined ordinal values. &lt;/p&gt;
&lt;h2 id=&quot;okay-so-whats-the-problem&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#okay-so-whats-the-problem&quot; aria-label=&quot;okay so whats the problem permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Okay? So What&apos;s the Problem?&lt;/h2&gt;
&lt;p&gt;Every time I&apos;ve ever seen an Ordinal declared, it&apos;s either trying to be first (&lt;em&gt;with a value of 0&lt;/em&gt;) or last (&lt;em&gt;with an absurdly high number&lt;/em&gt;). I have literally never seen an example where Ordinals are used to reasonably create an order of iteration. &lt;/p&gt;
&lt;div class=&quot;admonition admonition-caution alert alert--warning&quot;&gt;&lt;div class=&quot;admonition-heading&quot;&gt;&lt;h5&gt;&lt;span class=&quot;admonition-icon&quot;&gt;&lt;svg xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/span&gt;Hot Take&lt;/h5&gt;&lt;/div&gt;&lt;div class=&quot;admonition-content&quot;&gt;&lt;p&gt;Ordinals are bad.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h1 id=&quot;a-better-approach&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#a-better-approach&quot; aria-label=&quot;a better approach permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;A Better Approach&lt;/h1&gt;
&lt;p&gt;The thing about that iteration example above is that it looks pretty familiar. It&apos;s an example of a pretty classic computer science data structure, the &lt;a href=&quot;https://en.wikipedia.org/wiki/Directed_acyclic_graph&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Directed Acyclic Graph&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;The &lt;em&gt;most natural&lt;/em&gt; way to solve this problem &lt;strong&gt;is not&lt;/strong&gt; ordinals.  The most natural way to solve this problem is to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Build a direct graph where the vertices of the graph are &lt;code class=&quot;language-text&quot;&gt;TemplatePrimitiveInjectors&lt;/code&gt; with edges going from a dependent Injector to the Injector that must proceed it. &lt;/li&gt;
&lt;li&gt;Ensure the dependency graph is actually &lt;strong&gt;acyclic&lt;/strong&gt; -- meaning that there are no circular dependencies between Injectors. &lt;/li&gt;
&lt;li&gt;Perform a &lt;a href=&quot;https://en.wikipedia.org/wiki/Topological_sorting&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;topological sorting&lt;/a&gt; of the graph&lt;/li&gt;
&lt;li&gt;Iterate over the Injectors according to the sorted order&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Immediately after thinking up with this approach, my tenured engineering mind started yelling at me: &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Cool. Neat idea, Steven.  Is it really worth the level of effort to program this fancy approach just because it&apos;s &quot;better&quot;? Just hard code the ordering or use Ordinals like everyone else. The (Return on Investment) / (Level of Effort) Ratio is way too low. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&apos;m glad I didn&apos;t immediately cave.  My favorite part of all of this is just how easy it was to implement. &lt;/p&gt;
&lt;h2 id=&quot;step-1-build-the-graph&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#step-1-build-the-graph&quot; aria-label=&quot;step 1 build the graph permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Step 1: Build the Graph&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://jgrapht.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;JGraphT&lt;/a&gt; did most of the work for me on this one! &lt;/p&gt;
&lt;h3 id=&quot;finding-the-vertices&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#finding-the-vertices&quot; aria-label=&quot;finding the vertices permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Finding the Vertices&lt;/h3&gt;
&lt;p&gt;We said before that the graph&apos;s vertices would be the different &lt;code class=&quot;language-text&quot;&gt;TemplatePrimitiveInjector&lt;/code&gt;&apos;s. Thankfully, the Jenkins plugin ecosystem is built on the idea of extending and creating &lt;a href=&quot;https://javadoc.jenkins.io/hudson/ExtensionPoint.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ExtensionPoint&lt;/a&gt;&apos;s, so looking them up shouldn&apos;t be too hard.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;groovy&quot;&gt;&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TemplatePrimitiveInjector&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ExtensionPoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/**
   * fetches all registered TemplatePrimitiveInjectors
   *   
   * @return list of all TemplatePrimitiveInjectors
  */&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; ExtensionList&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;TemplatePrimitiveInjector&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Jenkins&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getExtensionList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;TemplatePrimitiveInjector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;token comment&quot;&gt;// shorthand for the interface methods that &lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// we&apos;re trying to define the order of execution for&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validateConfiguration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;injectPrimitives&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validateBinding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Invoking the static method &lt;code class=&quot;language-text&quot;&gt;TemplatePrimitiveInjector.all()&lt;/code&gt; returns the full list of registered Injectors. &lt;/p&gt;
&lt;h3 id=&quot;identifying-the-edges&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#identifying-the-edges&quot; aria-label=&quot;identifying the edges permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Identifying the Edges&lt;/h3&gt;
&lt;p&gt;Next up, we need a mechanism for identifying the dependencies. &lt;/p&gt;
&lt;p&gt;Functional Goals:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code class=&quot;language-text&quot;&gt;TemplatePrimitiveInjector&lt;/code&gt; interface has 3 stages and each stage may have its own dependencies, so we can&apos;t just define the dependencies on a class-wide basis. &lt;/li&gt;
&lt;li&gt;Injectors should be able to define this dependency with minimal effort&lt;/li&gt;
&lt;li&gt;Injectors should be able to define this dependency with minimal coupling to the rest of the code.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It turns out, a method-based annotation works perfect for this.&lt;/p&gt;
&lt;p&gt;Let&apos;s define a &lt;code class=&quot;language-text&quot;&gt;@RunAfter&lt;/code&gt; annotation that accepts as a parameter the class names of other Injectors that must be executed first.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;groovy&quot;&gt;&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;&lt;span class=&quot;token annotation punctuation&quot;&gt;@Retention&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;RUNTIME&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;         &lt;span class=&quot;token comment&quot;&gt;// be discoverable at runtime &lt;/span&gt;
&lt;span class=&quot;token annotation punctuation&quot;&gt;@Target&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ElementType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;METHOD&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// annotate methods (not class-wide)&lt;/span&gt;
@&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RunAfter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  
  &lt;span class=&quot;token comment&quot;&gt;/** define a list of other Injectors */&lt;/span&gt; 
  Class&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TemplatePrimitiveInjector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Through this annotation,  we can now easily construct dependencies.  Here&apos;s a short hand example: &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;groovy&quot;&gt;&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TemplatePrimitiveInjector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validateConfiguration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;injectPrimitives&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validateBinding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TemplatePrimitiveInjector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validateConfiguration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;token annotation punctuation&quot;&gt;@RunAfter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;A&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;injectPrimitives&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
 
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validateBinding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;C&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TemplatePrimitiveInjector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validateConfiguration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;injectPrimitives&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;token annotation punctuation&quot;&gt;@RunAfter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;A&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; B&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validateBinding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we just need a way to look up a method&apos;s dependency&apos;s:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;groovy&quot;&gt;&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * returns the dependencies listed in the RunAfter annotation if present
 * on the given Injector&apos;s methodname
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Class&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TemplatePrimitiveInjector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPrerequisites&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;TemplatePrimitiveInjector injector&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; String methodName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt; methodArgs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;TemplatePrimitiveInjector&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; prereqs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  MetaMethod metaMethod &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; injector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;metaClass&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pickMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;methodName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; methodArgs&lt;span class=&quot;token operator&quot;&gt;*.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;as&lt;/span&gt; Class&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;metaMethod &lt;span class=&quot;token keyword&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CachedMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Method method &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; metaMethod&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCachedMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    RunAfter annotation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAnnotation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;RunAfter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;annotation&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      prereqs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;annotation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flatten&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Class&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TemplatePrimitiveInjector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; prereqs
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;creating-the-graph&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#creating-the-graph&quot; aria-label=&quot;creating the graph permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Creating the Graph&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;groovy&quot;&gt;&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; Graph&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Class&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TemplatePrimitiveInjector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; DefaultEdge&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createGraph&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;String methodName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt; methodArgs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

  Graph&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Class&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TemplatePrimitiveInjector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; DefaultEdge&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; graph &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DefaultDirectedGraph&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;DefaultEdge&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  ExtensionList&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;TemplatePrimitiveInjector&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; injectors &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; TemplatePrimitiveInjector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// add each injector as a node in the graph&lt;/span&gt;
  injectors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;each&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; injector &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; graph&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addVertex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;injector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// for each injector, connect edges&lt;/span&gt;
  injectors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;each&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; injector &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;
    List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Class&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TemplatePrimitiveInjector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; prereqs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPrerequisites&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;injector&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; methodName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; methodArgs&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;    
    prereqs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;each&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; req &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;
      graph&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEdge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; injector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// check for infinite loops&lt;/span&gt;
  CycleDetector detector &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CycleDetector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;graph&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  Set&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;TemplatePrimitiveInjector&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; cycles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; detector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;findCycles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cycles&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JTEException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string gstring&quot;&gt;&quot;There are cyclic dependencies preventing initialization: &lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;cycles&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; graph

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;iterating-over-the-graph&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#iterating-over-the-graph&quot; aria-label=&quot;iterating over the graph permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Iterating over the Graph&lt;/h3&gt;
&lt;p&gt;Finally, we can create a method that takes a phase (either &lt;code class=&quot;language-text&quot;&gt;validateConfiguration&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;injectPrimitives&lt;/code&gt;, or &lt;code class=&quot;language-text&quot;&gt;validateBinding&lt;/code&gt;) and method arguments and ties all of this together! &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;groovy&quot;&gt;&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;invoke&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;String phase&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  
  List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Class&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TemplatePrimitiveInjector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; failedInjectors &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  Graph&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Class&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TemplatePrimitiveInjector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; DefaultEdge&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; graph &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createGraph&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;phase&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  AggregateException errors &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AggregateException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TopologicalOrderIterator&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;graph&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;each&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; injectorClazz &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;
    TemplatePrimitiveInjector injector &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; injectorClazz&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getDeclaredConstructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;newInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token comment&quot;&gt;// check if a dependent injector has failed, if so, don&apos;t execute&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPrerequisites&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;injector&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; phase&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;intersect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;failedInjectors&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        injector&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;invokeMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;phase&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;any&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;any&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      failedInjectors &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; injectorClazz
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// this phase failed throw an exception&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; errors
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h1 id=&quot;conclusion&quot; style=&quot;position:relative;&quot;&gt;&lt;a href=&quot;#conclusion&quot; aria-label=&quot;conclusion permalink&quot; class=&quot;anchor before&quot;&gt;&lt;svg aria-hidden=&quot;true&quot; focusable=&quot;false&quot; height=&quot;16&quot; version=&quot;1.1&quot; viewBox=&quot;0 0 16 16&quot; width=&quot;16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;I like to say that, &quot;the line between brilliant and hacky is often thin&quot;. Let me know which side you think this falls on in the comments :). &lt;/p&gt;
&lt;p&gt;Feel free to check out the actual code for this within the Jenkins Templating Engine: &lt;a href=&quot;https://github.com/jenkinsci/templating-engine-plugin/blob/2.0/src/main/groovy/org/boozallen/plugins/jte/init/primitives/TemplatePrimitiveInjector.groovy&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;TemplatePrimitiveInjector&lt;/a&gt;, &lt;a href=&quot;https://github.com/jenkinsci/templating-engine-plugin/blob/2.0/src/main/groovy/org/boozallen/plugins/jte/init/primitives/RunAfter.groovy&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;RunAfter&lt;/a&gt;, &lt;a href=&quot;https://github.com/jenkinsci/templating-engine-plugin/blob/2.0/src/main/groovy/org/boozallen/plugins/jte/init/primitives/TemplateBindingFactory.groovy&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;TemplateBindingFactory&lt;/a&gt;&lt;/p&gt;</content:encoded></item></channel></rss>