<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="https://havenga.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://havenga.dev/" rel="alternate" type="text/html" /><updated>2023-05-03T16:41:06+02:00</updated><id>https://havenga.dev/feed.xml</id><title type="html">Havenga.dev</title><author><name>Gregory Havenga</name></author><entry><title type="html">Strange Bundler sub-dependency source selection</title><link href="https://havenga.dev/bundler/strange-bundler-sub-dependency-source-selection/" rel="alternate" type="text/html" title="Strange Bundler sub-dependency source selection" /><published>2022-01-03T16:38:00+02:00</published><updated>2022-01-03T16:38:00+02:00</updated><id>https://havenga.dev/bundler/strange-bundler-sub-dependency-source-selection</id><content type="html" xml:base="https://havenga.dev/bundler/strange-bundler-sub-dependency-source-selection/">&lt;p&gt;Bundler implicit sub-dependencies prefer to pull from their highest declared non-default source, and it’s an absolute pain…&lt;/p&gt;

&lt;h2 id=&quot;context&quot;&gt;Context&lt;/h2&gt;

&lt;p&gt;If you’ve done any heavy enterprise Ruby work with shared features across multiple systems, logic that multiple systems need to be aware of simultaneously, or alternatively built some kind of internal cli tool in ruby, then you’ve probably built a gem in the past. There’s a further chance that you might not have been ready to opensource that particular gem for some reason, and thus opted to host it on some form of alternative gem registry. Gem Fury, Artifactory, or a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem server&lt;/code&gt; probably.&lt;/p&gt;

&lt;p&gt;Around the beginning of 2021 it was realized that Bundler was vulnerable to something referred to as &lt;a href=&quot;https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610&quot;&gt;dependency confusion&lt;/a&gt;. While I won’t go too far into depth about about this attack, the TL:DR version is that when declaring a Gemfile, in the past Bundler would prioritize gems found on the default source &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubygems.org&lt;/code&gt;. If you then added an additional source for some kind of internal gem, someone could release a opensource gem using the same name as your internal gem, which Bundler would then install instead of pulling your gem from your specified source.&lt;/p&gt;

&lt;p&gt;While there were protections in place on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubygems.org&lt;/code&gt; to prevent malicious gems from being uploaded, this was still a hazardous outcome from the perspective of being, at worst as possible RCE, at best, still a rather massive nuisance.&lt;/p&gt;

&lt;p&gt;So Bundler was &lt;a href=&quot;https://bundler.io/blog/2021/02/15/a-more-secure-bundler-we-fixed-our-source-priorities.html&quot;&gt;patched&lt;/a&gt; to make it prefer the explicitly declared gem source for a particular gem over the default gem source. Great! In almost all cases, this is probably exactly the behaviour you want. But, it comes with a caveat…&lt;/p&gt;

&lt;h2 id=&quot;the-problem&quot;&gt;The Problem&lt;/h2&gt;

&lt;p&gt;Bundler is a magnificent piece of software for helping ensure versions are locked at their intended versions, matched according to their dependency version specifications, and pulled from their effective sources, all without needing to be needlessly verbose about every single little thing that your app needs to run. If you’ve ever taken a peek at the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile.lock&lt;/code&gt; file in a fresh rails app you might have been surprised to find close to or even over 100 dependencies, despite the default Gemfile declaring 15 or so. The reason for this is when a gem is created, a gemspec file is written up containing gem dependencies and versions for those dependencies that the gem should be able to work with. This is the information which Bundler combines with the declared version constraints within your project’s Gemfile in order to grab any dependencies for the gems you declare to make sure they work as intended, without you needing to explicitly declare the sub-dependencies of your dependencies yourself in your Gemfile. Brilliant right?&lt;/p&gt;

&lt;p&gt;Now, here’s the question. If I declare a gem in my Gemfile, and configure it to come from a non-standard source, where do you expect that gem’s dependencies to come from?&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://rubygems.org&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;rspec&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;work-logic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;source: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://internal-gem-repo.awesome&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Intuitively, you might have guessed that the logical source of a gem’s dependencies would the same place as the gem itself right? So if I have a gem called, for example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;work-logic&lt;/code&gt;, declared to come from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://internal-gem-repo.awesome&lt;/code&gt;, we would expect any dependencies of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;work-logic&lt;/code&gt; to similarly be sourced from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://internal-gem-repo.awesome&lt;/code&gt;. Makes perfect sense.&lt;/p&gt;

&lt;figure class=&quot;&quot;&gt;
  &lt;img src=&quot;/assets/images/2021/01/03/dependencies%201.png&quot; alt=&quot;Source Graph for above Gemfile&quot; /&gt;
  
    &lt;figcaption&gt;
      rspec from rubygems, work-logic from internal gem repo.

    &lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Now, what if one of the dependencies of my internal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;work-logic&lt;/code&gt; gem is a public gem? Perhaps the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;work-logic&lt;/code&gt; gem is actually a Rails engine, and thereby actually depends on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt;.&lt;/p&gt;

&lt;figure class=&quot;&quot;&gt;
  &lt;img src=&quot;/assets/images/2021/01/03/dependencies%202.png&quot; alt=&quot;Source Graph for above Gemfile, with rails a dependency&quot; /&gt;
  
    &lt;figcaption&gt;
      Rails as a dependency of work-logic

    &lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Well, with the above behaviour, because &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;work-logic&lt;/code&gt; is sourced from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://internal-gem-repo.awesome&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt; is a dependency of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;work-logic&lt;/code&gt;, bundler is going to try source &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt; from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://internal-gem-repo.awesome&lt;/code&gt;. And if you couldn’t be bothered to mirror &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt; from your gem server, then it’s going to fail.&lt;/p&gt;

&lt;p&gt;“So just declare the rails source as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubygems.com&lt;/code&gt; explictly then…” you’re probably thinking.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://rubygems.org&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;rspec&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;rails&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;work-logic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;source: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://internal-gem-repo.awesome&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;figure class=&quot;&quot;&gt;
  &lt;img src=&quot;/assets/images/2021/01/03/dependencies%203.png&quot; alt=&quot;Source Graph for above Gemfile, with rails as dependency, explicitly declared&quot; /&gt;
  
    &lt;figcaption&gt;
      Rails as a dependency of work-logic, explicitly sourced from rubygems.org

    &lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;And that does indeed work, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt; is then explicitly sourced from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubygems.org&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;work-logic&lt;/code&gt; is explicitly sourced from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://internal-gem-repo.awesome&lt;/code&gt;, and all sounds swell until we hit the next reason bundler fails. Rails has dependencies. Lots of them. So, in a situation where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;work-logic&lt;/code&gt; depends on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt; but is sourced from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://internal-gem-repo.awesome&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt;, explicitly requested from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubygems.org&lt;/code&gt;, has all of it’s own implicit dependencies like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;active-record&lt;/code&gt;; where would you expect &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt;’s dependencies to be sourced from? Intuitively, one would probably say “well, those are rails dependencies, so those should come from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubygems.org&lt;/code&gt;.” Except that’s not what Bundler does. Bundler sees the sub dependencies of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt; in this situation as part of the initial &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;work-logic&lt;/code&gt; dependency tree. So even though we’ve explicitly requested &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt; be sourced from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubygems.org&lt;/code&gt;, bundler attempts to source &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt;’s dependencies from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://internal-gem-repo.awesome&lt;/code&gt;. Since there are mirrored versions of those gems on the internal gem repo, this technically works, but is not the desired outcome of our configuration.&lt;/p&gt;

&lt;figure class=&quot;&quot;&gt;
  &lt;img src=&quot;/assets/images/2021/01/03/dependencies%205.png&quot; alt=&quot;Source Graph for above Gemfile, with rails as an explicitly declared dependency, but it has it's own dependencies that still come from the internal gem repo&quot; /&gt;
  
    &lt;figcaption&gt;
      Rails as an explicitly declared dependency of work-logic but sourced from ruby gems, but it’s implicit gems still source from our internal gem repo.

    &lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;As it stands, there’s only 2 ways to fix this, some dependency jiggery pokery and potentially some manual modification of your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gemfile.lock&lt;/code&gt; to try force these sub-dependencies to source from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubygems.org&lt;/code&gt;, or explicitly declare every single gem that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt; depends on in your Gemfile and it’s source as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubygems.org&lt;/code&gt;, eschewing much of the simplicity the initial Gemfile would have offered.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://rubygems.org&quot;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;rspec&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;rails&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;active-record&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;action-text&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;active-support&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;etc&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;work-logic&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;source: &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;https://internal-gem-repo.awesome&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Ew…&lt;/p&gt;

&lt;h2 id=&quot;the-rough-solution&quot;&gt;The (Rough) Solution&lt;/h2&gt;

&lt;p&gt;Simply, in the process of generating the dependency graph and figuring out the versions and sources of gems, the closest explicitly declared parent of a gem should be used to determine it’s source. So as per the above context, the moment we declare &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt; explicitly, as being sourced from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubygems.org&lt;/code&gt;, any implicit dependencies of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt; should then be sourced from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubygems.org&lt;/code&gt; as well, unless a more explicit directive on one of those lower dependencies is given.&lt;/p&gt;

&lt;p&gt;I spent… &lt;strong&gt;way&lt;/strong&gt; too much time reading the bundler source code to figure out why this was happening, but I think I’ve tracked down the problem to the &lt;a href=&quot;https://github.com/rubygems/rubygems/blob/1c8c02b45db892a834d517eb64474d52311ff4c1/bundler/lib/bundler/source_map.rb&quot;&gt;SourceMap implementation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can delve into the depths of Bundler if you’re interested, but the issue seems to boil down to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Bundler:SourceMap#all_requirements&lt;/code&gt; method’s solution for resolving the source of indirect dependencies after the first pass of direct dependency resolution.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;all_requirements&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;direct_requirements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dup&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;unmet_deps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;non_default_explicit_sources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;spec_names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pinned_spec_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indirect_dependency_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;previous_source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indirect_dependency_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;previous_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indirect_dependency_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;no_ambiguous_sources&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Bundler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;feature_flag&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;bundler_3_mode?&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;The gem '&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indirect_dependency_name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;' was found in multiple relevant sources.&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;concat&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;previous_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;  * &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;sort&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;You &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;no_ambiguous_sources&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:must&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:should&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; add this gem to the source block for the source you wish it to be installed from.&quot;&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SecurityError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;no_ambiguous_sources&lt;/span&gt;
        &lt;span class=&quot;no&quot;&gt;Bundler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ui&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;warn&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Warning: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;unmet_deps&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;default_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_dependency_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unmet_deps&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flatten&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;direct_requirements&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@direct_requirements&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;begin&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{}&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;default_source&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;dependencies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dep&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;dep_source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;default&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;dep_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;add_dependency_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dep_source&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;To contextualize the snippet, the SourceMap defines the fully realized set of dependencies and where they need to be pulled from for Bundler to ultimately resolve and pull your gems. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;direct_requirements&lt;/code&gt; looks at the list of dependencies you explicitly defined in your Gemfile, and if the Gem has an explicitly defined source by virtue of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; kwarg or a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; block wrapping it’s definition in your Gemfile, then the source is defined. This dsl is evaluated to create a bundler &lt;a href=&quot;https://github.com/rubygems/rubygems/blob/1c8c02b45db892a834d517eb64474d52311ff4c1/bundler/lib/bundler/definition.rb&quot;&gt;Definition&lt;/a&gt;, which prior to resolution, generates a SourceMap using the above snippet to determine which source your respective gem will come from.&lt;/p&gt;

&lt;p&gt;The problem however, is in these few lines:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;all_requirements&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;direct_requirements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dup&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;unmet_deps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;non_default_explicit_sources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;spec_names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pinned_spec_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indirect_dependency_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;previous_source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indirect_dependency_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;previous_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indirect_dependency_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Bundler is grabbing unmet dependencies by cycling through your explicitly declared, non-default sources. Which is to say any gem source you’ve declared at all, that’s not RubyGems. It then goes through the spec list from that source, excluding any gems you explicitly declared in your Gemfile, and if that gem was already declared with a source elsewhere (such as your Gemfile or Gemfile.lock), it will just use that source. Otherwise it will just select the source it’s currently looking at as the source for your gem. This isn’t an illogical choice. If your gem is coming from this alternative repository, and your gem’s dependencies are present on this mirror, it’s probably reasonable you want your gem’s dependencies to come from there too.&lt;/p&gt;

&lt;p&gt;The problem is, per our example above, we’ve explicitly declared &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt; with the expectation that it’s dependencies should be sourced from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubygems.org&lt;/code&gt;, but because some version of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt;’s dependencies exist on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://internal-gem-repo.awesome&lt;/code&gt;, it’s prefering our explicitly declared gem source anyways.&lt;/p&gt;

&lt;p&gt;So step one to fixing this bizarre behaviour, is to make Bundler explicitly prefer to fetch a gems dependencies from it’s closest explicitly declared parent:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;all_requirements&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;direct_requirements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dup&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;direct_dependency_parents&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@dependencies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dep&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dependencies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sub_dep&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sub_dep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flatten&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;unmet_deps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;non_default_explicit_sources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;spec_names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pinned_spec_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indirect_dependency_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indirect_dependency_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;direct_dependency_parents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indirect_dependency_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Without modifying bundler in crazy ways, the most straightforward way to do this is to take the list of explicit dependencies we’re trying to configure a SourceMap for and map through them to create a hash of the sub_dependencies as keys, and their values being the gem that depends on them, so that we can establish the parent of that gem. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requirements&lt;/code&gt; variable is a hash list of our explicitly declared gems and what their source is, already made by the SourceMap class. So we leverage this with our new method of determing the parent gem for our current gem, and use that to determine the source.&lt;/p&gt;

&lt;p&gt;This largely resolves our issue, as now any gems that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt; depends on are sourced from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rubygem.org&lt;/code&gt; despite &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rails&lt;/code&gt; being a dependency of our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;work-logic&lt;/code&gt; gem. However, now that we’re here, we may go a step further. Our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;work-logic&lt;/code&gt; gem has some other dependencies that we don’t actually want to be sourced from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://internal-gem-repo.awesome&lt;/code&gt; as these are other open source gems we don’t have any good reason to mirror either. So I added an additional option to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem&lt;/code&gt; command of the Gemfile dsl, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preffered_spec_source&lt;/code&gt;. This required 3 particular changes.&lt;/p&gt;

&lt;p&gt;First we add our preferred_spec_source as a variable to the Dependency object Bundler uses to track dependencies.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Dependency&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Gem&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Dependency&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:autorequire&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;attr_reader&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:groups&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:platforms&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:gemfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:git&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:github&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:branch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:preferred_spec_source&lt;/span&gt;    

    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;initialize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{},&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;blk&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:runtime&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;vi&quot;&gt;@autorequire&lt;/span&gt;    &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;nil&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@groups&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;group&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:default&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:to_sym&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@source&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;source&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@preferred_spec_source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;preferred_spec_source&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@git&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;git&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@github&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;github&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@branch&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;branch&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@ref&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;ref&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@platforms&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;platforms&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@env&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;env&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@should_include&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;should_include&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kp&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;vi&quot;&gt;@gemfile&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;gemfile&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

      &lt;span class=&quot;vi&quot;&gt;@autorequire&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;require&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;key?&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;require&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Then we add &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preferred_spec_source&lt;/code&gt; as a VALID_KEY for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem&lt;/code&gt; command in the DSL.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Bundler&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Dsl&lt;/span&gt;
    &lt;span class=&quot;kp&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RubyDsl&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;evaluate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gemfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lockfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;eval_gemfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gemfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_definition&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;lockfile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;VALID_PLATFORMS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Bundler&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Dependency&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;PLATFORM_MAP&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;freeze&lt;/span&gt;

    &lt;span class=&quot;no&quot;&gt;VALID_KEYS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;sx&quot;&gt;%w[group groups git path glob name branch ref tag require submodules
                    platform platforms type source install_if gemfile preferred_spec_source]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;freeze&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;And then we need to modify the SourceMap implementation one last time so that, in the event the gem we’re looking at has an explicit parent, we need to check if the explicit parent declared a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preferred_spec_source&lt;/code&gt;, and use that instead of anything we might have determined elsewise.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;all_requirements&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;direct_requirements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dup&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;direct_dependency_parents&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@dependencies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dep&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;to_spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;dependencies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sub_dep&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sub_dep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dep&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;compact&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;flatten&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:merge&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

      &lt;span class=&quot;n&quot;&gt;unmet_deps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;non_default_explicit_sources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;spec_names&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pinned_spec_names&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;each&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indirect_dependency_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
          &lt;span class=&quot;n&quot;&gt;previous_source&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indirect_dependency_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;previous_source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;nil?&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;explicit_parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@dependencies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dependency&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dependency&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;direct_dependency_parents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indirect_dependency_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
            &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indirect_dependency_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;explicit_parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;explicit_parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;preferred_spec_source&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;sources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;rubygems_sources&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;rubygems repository &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;explicit_parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;preferred_spec_source&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;
              &lt;span class=&quot;n&quot;&gt;requirements&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;direct_dependency_parents&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;indirect_dependency_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
          &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This particular implementation is probably flaky as it does no validation that your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;preferred_spec_source&lt;/code&gt; isn’t garbage. (If it is, it seems to just ignore it.) With some refactoring and testing though,this would likely handle this particular situation robustly. I’d need to understand the bundler ecosystem far more than I do after my 2 day deep dive to do that though.&lt;/p&gt;

&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Figuring out how this functionality worked without ever having looked at how Bundler works internally has been quite the adventure. I’d like to take a moment to thank &lt;a href=&quot;https://github.com/pry/pry&quot;&gt;binding.pry&lt;/a&gt; for being the best developer tool I could ever recommend, as figuring this out ultimately required me to pull the bundler source code and just start jumping into various locations during the execution to determine the origin of my undesired source mappings. I’ve definitely gained a massive appreciation for the sheer complexity of what it is Bundler does so that we can, under 99% of all cases, just pull a bunch of correctly versioned and platformed gems from the right place, and move on with our lives without trudging through &lt;a href=&quot;https://en.wikipedia.org/wiki/Dependency_hell&quot;&gt;dependency hell&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These fixes are likely very naive in some regards, and will probably need to be refactored and tested to truly be useable in the wild so modify bundler at your own risk. I might consider opening a PR for bundler with a request for some guidance on any potential issues these changes could cause and how to prevent them.&lt;/p&gt;</content><author><name>Gregory Havenga</name></author><category term="bundler" /><summary type="html">Bundler implicit sub-dependencies prefer to pull from their highest declared non-default source, and it’s an absolute pain…</summary></entry><entry><title type="html">Deterministic software development practices and the horror of losing your Docker images</title><link href="https://havenga.dev/docker/determinism/debugging/horrors-of-image-loss/" rel="alternate" type="text/html" title="Deterministic software development practices and the horror of losing your Docker images" /><published>2021-01-14T18:00:00+02:00</published><updated>2021-01-14T18:00:00+02:00</updated><id>https://havenga.dev/docker/determinism/debugging/horrors-of-image-loss</id><content type="html" xml:base="https://havenga.dev/docker/determinism/debugging/horrors-of-image-loss/">&lt;p&gt;Sometimes when developing software, it can be hard to expect or account for some of the horrors that might arise as a result of a small and seemingly insignificant decision. Small, ostensibly innocuous decisions can have monumental ramifications down the line, as I’m certain any software developer with a few production systems under their belt can attest with a harrowed grimace. Over the past two days I had the joy of experiencing exactly such a consequence.&lt;/p&gt;

&lt;h2 id=&quot;deterministic-software-development-practices&quot;&gt;Deterministic Software Development Practices&lt;/h2&gt;

&lt;p&gt;While I continue the joys of pandemic-era South African enforced teetotalism despite my frayed nerves, it’s worth providing some context to my sudden desire to take a 6 month sabatical as a barista &lt;del&gt;or an alcoholic&lt;/del&gt;.&lt;/p&gt;

&lt;p&gt;Good software development practice emphasises determinism and reproducability when it comes to the code you write, the system you develop and the applications you deploy. In an ideal world, all code does the same thing every time, every system deploys identically every time, and applications only do what you expect them to every time.&lt;/p&gt;

&lt;p&gt;Much to our misery, such an ideal world is the stuff of fairy tales, and it’s only through the rigourous implementation of protocols and procedures that we can enforce some degree of reasonable expectation upon reality. Such efforts are the spawning pool of concepts such as Unit Testing, Test Driven Development (TTD), codefied deployments, Continuous Integration (CI) and Continuous Delivery (CD), Docker, and Infrastructure As Code, among others.&lt;/p&gt;

&lt;p&gt;Effectively tested code provides a valuable safety net of surety that running that code will produce the desired results when executed as intended; and if you’re testing effectively, that it will fail in a reliable and expected fashion when executed in an unexpected way.&lt;/p&gt;

&lt;p&gt;Codefied deployments with tools like Ansible, Chef or Salt are an effective means to remove the human element of error from potentially lengthy and complicated processes; making an invaluable assurance that your deployment processes will continue to work in a consistent manner even after John the DevOps whiz leaves for his new job in Silicon Valley (or quits software development entirely in favour of a more relaxing life as a carpenter).&lt;/p&gt;

&lt;p&gt;This testing and sets of strictly defined deployments can be further established as a deterministic process through the introduction of effective CI and CD, which can ensure that all code written is held to an appropriate testing standard, validating that no past tests have been broken (and in better implementations, that newly added code is effectively tested as well), and that deployments follow an expected deployment procedure based on the merging and versioning of code to various version control branches.&lt;/p&gt;

&lt;p&gt;This would normally take the form of the codefied deployment being executed with the newest versioned set of code pushed to a specific git branch, probably called “develop”, to a “Staging” server for manual validation and testing; while a versioned merge to a “&lt;del&gt;master&lt;/del&gt;main” branch would trigger the deployment of that code to “Production”, with all manner of potential checks and balances to ensure a safe and succesful deployment… if you’re not too scared of the word “DevOps”.&lt;/p&gt;

&lt;p&gt;The result of all these efforts is that you can ensure that whatever code you have deployed is, at the minimum, exactly as or more functional than it was in any prior deployment, and that in the event of disaster, you can effectively recover by following the same preceduralized steps, and deploy a functioning version of your application should your manager/boss/product owner/client demand it be done. (Though we do drift ever so slightly closer to that mystical land of fairytales here, as disaster recovery is rarely quite so straightforward.)&lt;/p&gt;

&lt;h2 id=&quot;the-horror-of-losing-your-docker-images&quot;&gt;The Horror of losing your Docker images&lt;/h2&gt;

&lt;p&gt;This all brings us to my past 2 days, having enjoyed the safety net of stable CI/CD which would reliably run the unit tests suite for my applications; and in the event of succesful testing and merging, build the associated application’s docker image to be stored in our internal registry for later deployment. (No automated deployments just yet, but the dream lives on for the moment.)&lt;/p&gt;

&lt;p&gt;For the uninitiated, Docker is a nifty containerization protocol that took the developer world by storm as an effective way to package up both your application and the environment in which it needs to be executed in, thereby ensuring yet another layer of determinism and an amusing and succinct response to the problem so well known by the phrase “it works on my machine.” Which is to say: “Well, then we’ll package your machine.”&lt;/p&gt;

&lt;p&gt;In terms of deployment, having your historical docker containers tagged and stored is an effective measure in terms of disaster recovery, as it provides a relatively easy mechanism by which to roll back your exact deployment in the event of disaster (or an unfortunately dynsfunctional deployment.) It can also be highly beneficial for simplying the development of an application between developers who might have differing local environments as well.&lt;/p&gt;

&lt;p&gt;Today’s horror boiled down to one seemingly innocuous decision. Due to space constraints, it was decided that our internal container registry would not be backed up, as these images could easily be rebuilt through their dockerfile defintions in any case, right? Right. Absolutely. In fact, I had made various efforts to script up these build processes and attach them to our CD processes. So when disaster struck and our registry was lost, it seemed like no real calamity at first.&lt;/p&gt;

&lt;p&gt;But then there was a bug we needed to patch in one of our systems… A story in of itself, once we eventually tracked down the relevant issue, we applied the relevant patch to our code, wrote the related tests to ensure it’s validity, and pushed it up for the lovely automated scripts to validate and build the docker image for future deployment. All seemed like smooth sailing as we confidently awaited the opportunity to resolve this strange bug in our system with but a singular deployment; none the wiser to our impending stress level hike.&lt;/p&gt;

&lt;p&gt;We deployed the newly built and packaged docker image to our staging server as the vastly important final sanity check prior to deploying it to master; but were surprised to find that weirdly, the application suddenly refused to connect to the database. Hmmm. Peculiar. So the debugging questions began? Did you change something? Did I change something? Is something out of date? The error seemed to imply TLS certificate validation is failing; maybe lets rerun the cert generation script? Okay. Did that, certs are all emplaced and it’s still broken.&lt;/p&gt;

&lt;p&gt;What, oh what, could it possibly be? Our despondency only grew as we checked increasingly more minute details. Cert timestamps checked out. Were we mounting the certs to the correct containers. Seems so. Did they verify? Yes again. Did they match? Unfortunately, since we knew we needed to find another thing to check, yes. Each increasingly unlikely question was answered unequivocably with the word “yes”; leaving us to break for lunch with severe frustration and feeling increasingly nervous as we were asked once again for the status on the patch we’d been working on prior to this issue.&lt;/p&gt;

&lt;p&gt;Let it be said, the wonders of taking a break for lunch can do wonders for even the desperate, as once we came back, my colleague had begun thinking about the thing that we had changed and thought nothing of. All the docker images we’d rebuilt, and more specifically, the base docker images we had rebuilt and published to our registry on top of which our code would be layered and deployed. I found the idea completely unlikely, until he used the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker image history&lt;/code&gt; to examine the sub layers of our deployed image, vs those of the last known good image we had been previously running on staging. What became evident was that the underlying operating system layers of the newer image were also newer.&lt;/p&gt;

&lt;p&gt;This to be perfectly clear… is not very deterministic. The implication of this is that the base Ruby image we had previously built our internal base image atop of had been functionally modified, which meant that our new base images were not identical to the base images we’d had prior to the loss of our image registry’s contents. Rolling back to a Ruby image that had not been modified further proved this, as our mystical cert verfication error disappeared in a puff of smoke. This of course, left us feeling relieved, exhausted and, understandably, very concerned that we may be unable to trust our docker images to build and behave in a reliable, expected and deterministic fashion. Or at least for how long it takes to rebuild all our images and manually ensure that they still run in the expected fashion.&lt;/p&gt;

&lt;p&gt;It is frustrating to realize that this core &lt;strong&gt;versioned&lt;/strong&gt; Ruby image dependency would be changed right out from under us like this. While our internal image storage was shielding us from the results of this external influence, it’s worth understanding that any of your code or systems which rely on external dependency’s to build or deploy are at the mercy of decisions which may be made outside of your control, and may be actively deleterious to your own software development efforts. We need only refer back to the events of the &lt;a href=&quot;https://qz.com/646467/how-one-programmer-broke-the-internet-by-deleting-a-tiny-piece-of-code/&quot;&gt;left-pad&lt;/a&gt; debacle to recall how one small, meaningless dependency broke the internet.&lt;/p&gt;

&lt;p&gt;So the moral of the story is quite simple. Your build artifacts are important, and your deployment mechanism’s reliance on external entities is an active risk to the succesful continuation and operation of your application, and potentially the business itself. Backup your docker containers, and make sure that whatever images or dependencies you might be using as a base from which to build your applications are, to the best of your ability, cached internally to shield them from external influence, be it mistaken or actively malicious by bad actors.&lt;/p&gt;</content><author><name>Gregory Havenga</name></author><category term="docker" /><category term="determinism" /><category term="debugging" /><summary type="html">Sometimes when developing software, it can be hard to expect or account for some of the horrors that might arise as a result of a small and seemingly insignificant decision. Small, ostensibly innocuous decisions can have monumental ramifications down the line, as I’m certain any software developer with a few production systems under their belt can attest with a harrowed grimace. Over the past two days I had the joy of experiencing exactly such a consequence.</summary></entry><entry><title type="html">Building a Jekyll Blog as lazily as possible</title><link href="https://havenga.dev/jekyll/update/blog/building-a-jekyll-blog/" rel="alternate" type="text/html" title="Building a Jekyll Blog as lazily as possible" /><published>2020-07-16T17:03:00+02:00</published><updated>2020-07-16T17:03:00+02:00</updated><id>https://havenga.dev/jekyll/update/blog/building-a-jekyll-blog</id><content type="html" xml:base="https://havenga.dev/jekyll/update/blog/building-a-jekyll-blog/">&lt;p&gt;Many software developers have a personal blog that acts as something of a portfolio of the things they’ve worked on, researched or merely learned. What I am going to talk about is Jekyll and Github-Pages, two lovely pieces of technology which allowed me to put this little blog of my own together with the minimum effort possible.&lt;/p&gt;

&lt;p&gt;Now before I get into detail; yes, “lazily” and “minimum effort possible” are perhaps not terms you want to be reading if you’re trying to appraise the value and behavioural habits of a Software Developer. However in this case I use the terms not to imply that I’ve avoided putting care and attention into the creation of the blog, but rather to indicate a preference towards more efficient and effective solutions when pragmatically appropriate.&lt;/p&gt;

&lt;p&gt;In this case pragmatism speaks to the fact that while I am fully capable of fancy, frameworked front-end development, I don’t derive any particular enjoyment from it like I do with other developmental tasks. As such, the pragmatic path to facilitate me writing posts about the things I have a passion for, is to get this blog online as efficiently possible.&lt;/p&gt;

&lt;p&gt;I suppose this is something of a moot point, since I do find some joy in finding efficient and effective approaches to problems, so I somehow found my itch scratched despite the task at hand.&lt;/p&gt;

&lt;h2 id=&quot;finally-the-jekyll-stuff&quot;&gt;Finally, the Jekyll stuff&lt;/h2&gt;

&lt;p&gt;In any case, you’re probably not here to listen to me talk about myself, so lets talk about &lt;a href=&quot;https://jekyllrb.com/&quot;&gt;Jekyll&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;Jekyll is a Ruby Gem written with the express goal of minimizing the difficulty involved in writing and maintaining a blog by automating various aspects of common blog design and management, as well as providing a fairly simple and straightforward environment with which to apply blog themes kindly offered by the open-source community. (The link to the theme powering my blog in particular is present in the footer of this page.)&lt;/p&gt;

&lt;p&gt;As far as it’s goal goes, it beats the mark quite substantially. Like all Ruby Gem’s, installation and creation of your very own Jekyll project is a simple as running the following in your terminal, provided you have a reasonably modern version of Ruby available within your development environment.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;gem &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;jekyll &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; jekyll new &amp;lt;your_blog_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Likely your output from the jekyll new command will look something like this:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;➜  jekyll new &lt;span class=&quot;nb&quot;&gt;test
&lt;/span&gt;Running bundle &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; /home/gregory/Projects/test... 
  Bundler: Fetching gem metadata from https://rubygems.org/..........
  Bundler: Fetching gem metadata from https://rubygems.org/.
  Bundler: Resolving dependencies...
  Bundler: Using public_suffix 4.0.5
  Bundler: Using addressable 2.7.0
  Bundler: Using bundler 2.0.1
  Bundler: Using colorator 1.1.0
  Bundler: Using concurrent-ruby 1.1.7
  Bundler: Using eventmachine 1.2.7
  Bundler: Using http_parser.rb 0.6.0
  Bundler: Using em-websocket 0.5.1
  Bundler: Using ffi 1.13.1
  Bundler: Using forwardable-extended 2.6.0
  Bundler: Using i18n 0.9.5
  Bundler: Using rb-fsevent 0.10.4
  Bundler: Using rb-inotify 0.10.1
  Bundler: Using sass-listen 4.0.0
  Bundler: Using sass 3.7.4
  Bundler: Using jekyll-sass-converter 1.5.2
  Bundler: Using listen 3.2.1
  Bundler: Using jekyll-watch 2.2.1
  Bundler: Using kramdown 1.17.0
  Bundler: Using liquid 4.0.3
  Bundler: Using mercenary 0.3.6
  Bundler: Using pathutil 0.16.2
  Bundler: Using rouge 3.22.0
  Bundler: Using safe_yaml 1.0.5
  Bundler: Using jekyll 3.8.7
  Bundler: Using jekyll-feed 0.15.0
  Bundler: Using jekyll-seo-tag 2.6.1
  Bundler: Using minima 2.5.1
  Bundler: Bundle &lt;span class=&quot;nb&quot;&gt;complete&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; 6 Gemfile dependencies, 28 gems now installed.
  Bundler: Use &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;bundle info &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;gemname]&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; to see where a bundled gem is installed.
New jekyll site installed &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; /home/gregory/Projects/test. 
➜  &lt;span class=&quot;nb&quot;&gt;cd test&lt;/span&gt;
➜  &lt;span class=&quot;nb&quot;&gt;ls
&lt;/span&gt;404.html  about.md  _config.yml  Gemfile  Gemfile.lock  index.md  _posts
➜  &lt;span class=&quot;nb&quot;&gt;ls &lt;/span&gt;_posts 
2020-08-16-welcome-to-jekyll.markdown

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

&lt;p&gt;Congrats, you have a Jekyll blog! Well done! Lets knock off, head to the pub and share a pint in celebration! All you need to do is start it:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;➜ jekyll s
Configuration file: /home/gregory/Projects/test/_config.yml
            Source: /home/gregory/Projects/test
       Destination: /home/gregory/Projects/test/_site
 Incremental build: disabled. Enable with &lt;span class=&quot;nt&quot;&gt;--incremental&lt;/span&gt;
      Generating... 
       Jekyll Feed: Generating feed &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;posts
                    &lt;span class=&quot;k&quot;&gt;done in &lt;/span&gt;0.505 seconds.
 Auto-regeneration: enabled &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'/home/gregory/Projects/test'&lt;/span&gt;
    Server address: http://127.0.0.1:4000/
  Server running... press ctrl-c to stop.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;figure class=&quot;&quot;&gt;
  &lt;img src=&quot;/assets/images/2020/07/16/new_jekyll_blog.png&quot; alt=&quot;how the new Jekyll blog homepage looks&quot; /&gt;
  
    &lt;figcaption&gt;
      Your wonderful new Jekyll blog.

    &lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Okay, okay, odds are good you’re fully aware I’m being full of nonsense and that, unfortunately, nothing is that simple. (As much as us lazy developers wish otherwise…)&lt;/p&gt;

&lt;p&gt;Now that we’ve had our reality check, lets get on with actually turning our initial generated jekyll template into an actual blog. Naturally, nothing beats the actual documentation which is likely to be updated in the long term unlike this blog post, so you can find that &lt;a href=&quot;https://jekyllrb.com/docs/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;the-lazyiest-way-to-make-the-blog-your-own&quot;&gt;The lazyiest way to make the blog your own&lt;/h2&gt;

&lt;p&gt;First and foremost, as clean as the default aesthetic is, you’re undoubtedly in the mood to show off your creative flare and make sure your blog effectively mirrors your sunshiny disposition and meticulous attention to design and detail… or, like me, you just don’t want to burn your readers eyeballs out while being minimally offensive from a design perspective.&lt;/p&gt;

&lt;p&gt;The following is the default configuration for Jekyll generated by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jekyll new&lt;/code&gt; command. The is essentially the central configuration underpinning the entire Jekyll blog. The blog’s title, url and it’s wonderful, skillful and not at all lazy author are all detailed in here, in addition to various other interesting configurations, including those specific to the theme you apply to your blog.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# Welcome to Jekyll!&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# This config file is meant for settings that affect your whole blog, values&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# which you are expected to set up once and rarely edit after that. If you find&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# yourself editing this file very often, consider using Jekyll's data files&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# feature for the data you need to update frequently.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# For technical reasons, this file is *NOT* reloaded automatically when you use&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# 'bundle exec jekyll serve'. If you change this file, please restart the server process.&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Site settings&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# These are used to personalize your new site. If you look in the HTML files,&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# you will see them accessed via Havenga.dev, your-email@example.com, and so on.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# You can create any custom variable you would like, and they will be accessible&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# in the templates via .&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Your awesome title&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;your-email@example.com&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;&amp;gt;-&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# this means to ignore newlines until &quot;baseurl:&quot;&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;Write an awesome description for your new site here. You can edit this&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;line in _config.yml. It will appear in your document head meta (for&lt;/span&gt;
  &lt;span class=&quot;s&quot;&gt;Google search results) and in your feed.xml site description.&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;baseurl&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# the subpath of your site, e.g. /blog&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# the base hostname &amp;amp; protocol for your site, e.g. http://example.com&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;twitter_username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;jekyllrb&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;github_username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;s&quot;&gt;jekyll&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Build settings&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;markdown&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;kramdown&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;theme&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;minima&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;jekyll-feed&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Exclude from processing.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# The following items will not be processed, by default. Create a custom list&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# to override the default setting.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# exclude:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   - Gemfile&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   - Gemfile.lock&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   - node_modules&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   - vendor/bundle/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   - vendor/cache/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   - vendor/gems/&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#   - vendor/ruby/&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;You’ll notice the config contains a line written “theme: minima”, which applies the “minima” theme to your blog, no extra steps required. Changing the theme is simply a matter of making sure the theme files are available and then modifying this particular configuration. There’s a variety of wonderful open source themes to view and choose from, as well as details about how to make sure your gem based themes are available to your blog, all mentioned in the Jekyll theme documentation &lt;a href=&quot;https://jekyllrb.com/docs/themes/&quot;&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;no-minimum-effort-prettiness&quot;&gt;&lt;del&gt;No&lt;/del&gt; Minimum effort prettiness&lt;/h2&gt;

&lt;p&gt;Of course, if you’re feeling the itch of laziness, then what you’ll be particularly interested in is this one subtle bit of configuration in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt; for this very blog (at the time of writing at any case):&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;remote_theme&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;mmistakes/minimal-mistakes@4.20.1&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;remote_theme&lt;/code&gt; configuration lets you, for specific themes that have been apropriately configured on Github to be supported, let you set them as your blogs chosen theme using this option, and then do literally nothing else in your pursuit of &lt;del&gt;laziness&lt;/del&gt; efficiency. Job’s done and your blog suddenly looks magically different give or take a few configuration tweaks specific to the theme you chose.&lt;/p&gt;

&lt;p&gt;Make sure to at least glance over the documentation for your theme since, for example, the &lt;a href=&quot;https://github.com/mmistakes/minimal-mistakes&quot;&gt;minimal-mistakes&lt;/a&gt; theme in particular requires the default layout of your pages to be changed in order for them to be able to be pre-rendered by Jekyll.&lt;/p&gt;

&lt;p&gt;From there, just a little config and you’re all set. Most of the themes including some magical logic that can use information provided in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_config.yml&lt;/code&gt; to populate all manner of fancy presentational details for you without you needing to write any HTML or CSS, such as the author sidebar with my handsome mug.&lt;/p&gt;

&lt;h2 id=&quot;deployment-time-oh-no&quot;&gt;Deployment time… Oh no.&lt;/h2&gt;

&lt;p&gt;Yep, now we actually have to put this puppy on the internet! Easy right? Maybe you could spool up a Heroku app, push your blog over to there and present it just like that? Or you could purchase a server or compute instance from some provider like AWS, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scp&lt;/code&gt; your code over to it and turn the ignition to immediate developer blogging success!&lt;/p&gt;

&lt;p&gt;Orrrrrrrrrrr, if you’ve put as much effort into building this blog as I have, these still sound like too much work. Luckily for us, &lt;a href=&quot;https://pages.github.com/&quot;&gt;Github Pages&lt;/a&gt; is a thing.&lt;/p&gt;

&lt;p&gt;Github Pages is an awesome little bit of functionality provided by Github to serve some perhaps not-very-basic informational webpages directly from the Github repository. In the case of this blog, this repository is found at &lt;a href=&quot;https://github.com/OddballGreg/oddballgreg.github.io&quot;&gt;https://github.com/OddballGreg/oddballgreg.github.io&lt;/a&gt;, which translates to the blog being hosted at &lt;a href=&quot;https://oddballgreg.github.io&quot;&gt;https://oddballgreg.github.io&lt;/a&gt; by Github Pages.&lt;/p&gt;

&lt;p&gt;You might notice that’s not the URL you’re at right now. I’d picked up &lt;a href=&quot;havenga.dev&quot;&gt;havenga.dev&lt;/a&gt; at a good price from Namecheap a while back when I first thought about writing a dev blog, and after a day of learning to understand what on earth ANAME and CNAME records are, as well as a variety of other DNS related tidbits, set that domain to permanently redirect to the Github Pages url for this blog.&lt;/p&gt;

&lt;p&gt;You’ll need to enable the Github Pages functionality (and inform Github of your desire to use your own URL if you care to) on the primary settings page of your repository. Make sure to enable HTTPS as well since, well, there’s really no reason your web assets shouldn’t be using HTTPS in this day and age. Your account will need to be verified and a few extra details filled in before GitHub will let you finish this configuration.&lt;/p&gt;

&lt;figure class=&quot;&quot;&gt;
  &lt;img src=&quot;/assets/images/2020/07/16/github_config.png&quot; alt=&quot;Github's Github Pages settings&quot; /&gt;
  
    &lt;figcaption&gt;
      The Github repository settings for enabling Github Pages

    &lt;/figcaption&gt;
  
&lt;/figure&gt;

&lt;p&gt;Next up; the following is the “Gemfile” for this blog. One of these is automatically generated by the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jekyll new&lt;/code&gt; command, though it’s not unique to blogs, or even web applications. Gemfiles are essentially Ruby’s way of performing dependency management. By listing the gems you want to use in your application and setting their specific versions as desired, you can effectively manage the dependencies and their versions that you expect to use when running the ruby application. Running the ruby command &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bundle&lt;/code&gt; in a modern ruby version (or installing it via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gem install&lt;/code&gt; in an older one first) will then trigger “bundler” to resolve the dependency requirements of each gem listed in the Gemfile to make sure that none of the dependencies have mutually exclusive requirements; before writing the final set of resolved and agreed dependency versions to the “Gemfile.lock” file. Provided you commit this file to your source control, it provides you with a relatively strong assurance that anyone running the Ruby project will be doing so with same dependencies.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;https://rubygems.org&quot;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Hello! This is where you manage which Jekyll version is used to run.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# When you want to use a different version, change it below, save the&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#     bundle exec jekyll serve&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# This will help ensure the proper Jekyll version is running.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# Happy Jekylling!&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# gem &quot;jekyll&quot;, &quot;~&amp;gt; 3.8.6&quot;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# If you want to use GitHub Pages, remove the &quot;gem &quot;jekyll&quot;&quot; above and&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# uncomment the line below. To upgrade, run `bundle update github-pages`.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;github-pages&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;group: :jekyll_plugins&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'faraday'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'0.17.3'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;jekyll-github-metadata&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'jekyll-include-cache'&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# If you have any plugins, put them here!&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:jekyll_plugins&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;jekyll-feed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;~&amp;gt; 0.6&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Windows does not include zoneinfo files, so bundle the tzinfo-data gem&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;# and associated library.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;install_if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;RUBY_PLATFORM&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=~&lt;/span&gt; &lt;span class=&quot;sr&quot;&gt;%r!mingw|mswin|java!&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tzinfo&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;~&amp;gt; 1.2&quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;tzinfo-data&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# Performance-booster for watching directories on Windows&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;wdm&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;~&amp;gt; 0.1.0&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:install_if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Gem&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;win_platform?&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Longwinded explanation aside, the only thing your really need to care about is that for Github Pages to work with your Jekyll blog, you need to add the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;github-pages&lt;/code&gt; gem as a dependecy for the application, and then run bundle to resolve a version for it.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;gem&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;github-pages&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;group: :jekyll_plugins&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Provided all has gone well, you should be all set to push your code to it’s Github repository now and set off to demonstrate your programming prowess to the world.&lt;/p&gt;

&lt;h2 id=&quot;all-done&quot;&gt;All Done!&lt;/h2&gt;

&lt;p&gt;Erh, not really. You realize you still have to actually write some content for the blog now right? Maybe a dry attempt at humorously describing how lazy Jekyll and Github Pages let you be while setting up your blog?&lt;/p&gt;

&lt;p&gt;In any case, I recommend taking it easy, putting on some calming music or YouTube video game playthroughs you don’t actually care to pay too much attention to, and just have fun with it. Having written for a blog every day for 2 years in my past, I can assure you, it’s all too easy to stress yourself out by applying too much pressure when it’s unneeded… kind’ve like trying to make a professional blog right off the bat when almost no-one is likely to be reading it in the beginning anyways.&lt;/p&gt;</content><author><name>Gregory Havenga</name></author><category term="jekyll" /><category term="update" /><category term="blog" /><summary type="html">Many software developers have a personal blog that acts as something of a portfolio of the things they’ve worked on, researched or merely learned. What I am going to talk about is Jekyll and Github-Pages, two lovely pieces of technology which allowed me to put this little blog of my own together with the minimum effort possible.</summary></entry></feed>