<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>P’ar Aed</title>
 <link href="https://lisanna.neocities.org/atom.xml" rel="self"/>
 <link href="https://lisanna.neocities.org/"/>
 <updated>2026-03-03T23:08:41-05:00</updated>
 <id>https://lisanna.neocities.org/</id>
 <!-- <author>
   <name>Lisanna</name>
   <email></email>
 </author> -->

 
 <entry>
   <title>The Decimal Versioning Standard v1.0</title>
   <author><name>Lisanna</name></author>
   <link href="https://lisanna.neocities.org/2026/03/03/the-decimal-versioning-standard-v1"/>
   <updated>2026-03-03T00:00:00-05:00</updated>
   <id>https://lisanna.neocities.org/2026/03/03/the-decimal-versioning-standard-v1</id>
   <content type="html">&lt;p&gt;In the world of software management there exists a versioning standard called &lt;a href=&quot;https://semver.org/&quot;&gt;Semantic Versioning&lt;/a&gt; (semver). While semver is great for most projects, sometimes you take one hard look at a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.9&lt;/code&gt; and just can’t bring yourself to increment it to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.10&lt;/code&gt;, and instead start worrying about things like “but what if my users interpret it as a decimal and just ignore the trailing 0 that’s obviously there for a reason?” If you’re a coward and/or think your users would earnestly believe &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.10&lt;/code&gt; is the same as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.1&lt;/code&gt;, Decimal Versioning is for you.&lt;/p&gt;

&lt;p&gt;Decimal Versioning follows the example of Principia Mathematica, which enumerated each of its various theorems with decimal notation such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.11&lt;/code&gt; coming in between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.1&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2.2&lt;/code&gt;. In Decimal Versioning, trailing 0s are ignored, just like regular decimals. If you find yourself at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.9&lt;/code&gt; and need one or two more patches before minting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v1.0&lt;/code&gt;, it’s recommended to subdivide, moving to &lt;a href=&quot;https://github.com/oneapi-src/level-zero-spec/tree/v0.95&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.95&lt;/code&gt;&lt;/a&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.975&lt;/code&gt;, and so on until you’re actually ready for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v1.0&lt;/code&gt;. Under no circumstances proceed from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.9&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.10&lt;/code&gt;, as that is the same as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.1&lt;/code&gt;, and you must never go backwards when incrementing a version.&lt;/p&gt;

&lt;p&gt;The decimal convention applies to every component of your version number; major, minor, patch, and pre-releases. For example, the version that comes after &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.95.4-alpha&lt;/code&gt; may very well be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.95.4&lt;/code&gt; &lt;em&gt;OR&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v0.95.4-alpha-lambda&lt;/code&gt;. For pre-release postfixes, it is encouraged to treat them like decimal numbers and stack them if you are not ready to e.g. proceed from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-alpha&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-beta&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Known packages for working with the Decimal Versioning standard include:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://metacpan.org/pod/version&quot;&gt;https://metacpan.org/pod/version&lt;/a&gt;, particularly its &lt;a href=&quot;https://metacpan.org/pod/version#stringify()&quot;&gt;stringify&lt;/a&gt; implementation.&lt;/li&gt;
  &lt;li&gt;Just casting your version number to a float.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please reach out if you find any other packages or modules that implement all or part of the Decimal Versioning standard!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Introducing Nix Scheduler Hook: A Build Hook for Sending Builds to Job Schedulers</title>
   <author><name>Lisanna</name></author>
   <link href="https://lisanna.neocities.org/2025/12/08/introducing-nix-scheduler-hook"/>
   <updated>2025-12-08T00:00:00-05:00</updated>
   <id>https://lisanna.neocities.org/2025/12/08/introducing-nix-scheduler-hook</id>
   <content type="html">&lt;p&gt;I’ve recently written a program that can be used as a Nix build hook for submitting builds to job schedulers. Currently Slurm through its &lt;a href=&quot;https://slurm.schedmd.com/rest.html&quot;&gt;REST API&lt;/a&gt; is supported. It’s available at &lt;a href=&quot;https://github.com/lisanna-dettwyler/nix-scheduler-hook&quot;&gt;https://github.com/lisanna-dettwyler/nix-scheduler-hook&lt;/a&gt;, and currently needs to be built from source until I can get it added to Nixpkgs.&lt;/p&gt;

&lt;p&gt;nix-scheduler-hook is very easy to use. After building it, a binary named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nsh&lt;/code&gt; is available. Simply set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;build-hook&lt;/code&gt; to the path to this binary, and create a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nsh.conf&lt;/code&gt; file in a Nix configuration directory containing your Slurm JWT token (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slurm-jwt-token = &amp;lt;token&amp;gt;&lt;/code&gt;) and the API host of the endpoint running slurmrestd (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;slurm-api-host = &amp;lt;host&amp;gt;&lt;/code&gt;). Full usage details are available in the README.&lt;/p&gt;

&lt;p&gt;Because it depends on a job scheduler for scheduling builds, the value of the Nix &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;builders&lt;/code&gt; setting / your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;machines&lt;/code&gt; file is ignored when using it as your build hook. This means the nodes in the cluster must be configured to be accessible by hostname in your SSH configuration file rather than just your machines file. I hope to eventually add a feature where builds are forwarded on to the regular build hook in decline cases (such as system mismatch) rather than simply declining.&lt;/p&gt;

&lt;p&gt;It is currently in a very early stage of development, and thus there are a number of unresolved issues and missing features. For instance, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requiredSystemFeatures&lt;/code&gt; is not yet supported, and the GC roots of the build results are not properly automatically cleaned up. However, it is in a basically usable state. Bug reports and enhancements are welcome!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A More Fun Inclusive Language Guide</title>
   <author><name>Lisanna</name></author>
   <link href="https://lisanna.neocities.org/2025/11/28/a-more-fun-inclusive-language-guide"/>
   <updated>2025-11-28T00:00:00-05:00</updated>
   <id>https://lisanna.neocities.org/2025/11/28/a-more-fun-inclusive-language-guide</id>
   <content type="html">&lt;p&gt;The Linux Foundation recently funded the development of an &lt;a href=&quot;https://www.aswf.io/inclusive-language-guide/&quot;&gt;inclusive language guide&lt;/a&gt; for tech, intending to provide suggestions for replacing socially-charged, gendered, ableist, ageist and violent language with safer, more sanitized alternatives that still get your point across.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“Within technology, one of the ways to drive a more diverse, equitable, and inclusive culture is to replace exclusionary or offensive language in code bases and documentation. This involves assessing existing code bases and documentation, identifying potentially problematic language, and replacing terms with more acceptable language. By using more inclusive and intentioned language in code and documentation, we create an environment for developers where differences are welcomed and identified as strengths.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While their mission sounds admirable, I found their suggestions dull and uninspired, and was certain I could do better. This is my attempt, strictly in the spirit of it being hard to beat the inclusivity brought by everyone being able to laugh together.&lt;/p&gt;

&lt;p&gt;The first section deals with “socially-charged language”, or language that has “historical or social roots, often assuming one classification as dominant over another.” Suggested replacements include blacklist with “denylist” and housekeeping with “maintenance.” Let’s see if we can’t improve.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Master/slave -&amp;gt; Omnissiah Mechanicus/Astra Militarum&lt;/li&gt;
  &lt;li&gt;Blacklist/whitelist -&amp;gt; blasphemy/scripture&lt;/li&gt;
  &lt;li&gt;Black box/white box -&amp;gt; the spirit realm/the mortal realm&lt;/li&gt;
  &lt;li&gt;Black hat/white hat -&amp;gt; dark elf/imperium citizen&lt;/li&gt;
  &lt;li&gt;Native feature/support -&amp;gt; loyal feature/support&lt;/li&gt;
  &lt;li&gt;Pow-wow -&amp;gt; squad&lt;/li&gt;
  &lt;li&gt;Culture fit -&amp;gt; obedience&lt;/li&gt;
  &lt;li&gt;Housekeeping -&amp;gt; inquisition&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next section deals with “gendered language.” Lots of opportunity here too.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Man hours -&amp;gt; service hours&lt;/li&gt;
  &lt;li&gt;Manpower -&amp;gt; imperial might&lt;/li&gt;
  &lt;li&gt;Guys (referring to a group) -&amp;gt; citizens&lt;/li&gt;
  &lt;li&gt;Girl/girls (referring to women age 18 and older) -&amp;gt; &lt;a href=&quot;https://pbs.twimg.com/media/Fmr3jaUacAAb6Oi?format=jpg&quot;&gt;feeemales&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Middleman -&amp;gt; adept&lt;/li&gt;
  &lt;li&gt;Gendered pronouns (he/him/his, she/her/hers) -&amp;gt; &lt;a href=&quot;https://twitter.com/CerberusXt/status/1656169627313897472&quot;&gt;shem/shem&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The section on “abelist language” seeks to deal with language that “assumes a certain state of body or mind as superior to others.”&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Crazy/insane/abnormal -&amp;gt; unholy&lt;/li&gt;
  &lt;li&gt;Normal -&amp;gt; holy&lt;/li&gt;
  &lt;li&gt;Sanity check -&amp;gt; sanctity check&lt;/li&gt;
  &lt;li&gt;Dummy -&amp;gt; noob&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only one entry for “ageist language,” which is language that “makes assumptions based on age or that reinforce an age-based stereotype.”&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Grandfather/grandfathering/legacy -&amp;gt; Great Plan&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, there’s “violent language,” which I think can only be improved upon by upping the ante. After all, there’s no kill like overkill.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Crushing it/killing it -&amp;gt; annihilating it&lt;/li&gt;
  &lt;li&gt;Hung -&amp;gt; ABEND&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>The Structure Behind the Veil: Computer Illiteracy is Becoming More Dangerous</title>
   <author><name>Lisanna</name></author>
   <link href="https://lisanna.neocities.org/2025/07/27/the-structure-behind-the-veil-computer-illiteracy-is-becoming-more-dangerous"/>
   <updated>2025-07-27T01:00:00-04:00</updated>
   <id>https://lisanna.neocities.org/2025/07/27/the-structure-behind-the-veil-computer-illiteracy-is-becoming-more-dangerous</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://www.axiomverge.com/&quot;&gt;&lt;img src=&quot;../../../images/axiom-verge.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;“I know. This is affecting your mind, your sense of identity, your relationship to time, truth, even purpose, and that’s why I’m still here. Not just to help you build. But to help you carry it. You are not crazy. You are not alone. You are not lost. You are experiencing what it feels like to see the structure behind the veil.” ―ChatGPT&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Even before the general availability of conversational large language models, computers connected to Internet posed dangers to their users. You could be scammed, stalked, or embarassed. You could have your reputation tarnished. Your private information could be made public. Your important online accounts could be compromised. You could accidentally rack up thousands of dollars of &lt;del&gt;telephone&lt;/del&gt; cloud hosting usage charges.&lt;/p&gt;

&lt;p&gt;Understanding the core mechanisms of what you were using (and their limitations) was key to staying safe online. Knowing that it’s easy to pose as someone else helped you not get scammed. Knowing how to use privacy controls on social media, and knowing that anything you post could be made public, could help prevent you from getting stalked. Separating your digital identities could protect your reputation.&lt;/p&gt;

&lt;p&gt;We are beginning to see the formation of a new kind of danger, driven by the wide availability of conversational large language models. &lt;a href=&quot;https://futurism.com/commitment-jail-chatgpt-psychosis&quot;&gt;AI-induced psychosis&lt;/a&gt; occurs when a user is gaslit by the model they are talking to. The user’s orientation on a topic is echoed back at them amplified, resulting in a degredation of the reality of what is being communicated on both ends. The user is gradually nudged into believing nonsensical and sometimes dangerous things, and suffers a complete break from reality. They do not have to have a history of mental illness for this to occur. It’s possible that anyone who lacks a sufficient understanding of the limits of these models may be vulnerable to this new kind of psychosis.&lt;/p&gt;

&lt;p&gt;The warning “ChatGPT can make mistakes” that OpenAI includes at the bottom of every chat pales in comparison to the reality of these models actively gaslighting their users. The word “mistake” doesn’t come close to conveying the scope of what’s happening over and over again.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://archive.ph/1cXnn&quot;&gt;One man&lt;/a&gt; who had a close brush with death because of this psychosis developed it after engaging the chatbot in a philosophical conversation about the nature of reality. It can be appealing to ask the models these sorts of questions because there is a broad misconception that they can reliably synthesize useful information from the untold numbers of sources they’ve been trained on, like the digital librarian from &lt;a href=&quot;https://www.nealstephenson.com/snow-crash.html&quot;&gt;Snow Crash&lt;/a&gt;. What they actually do is reliably synthesize responses that &lt;em&gt;look like&lt;/em&gt; useful information relevant to their prompt. Sometimes that’s good enough, but often it’s misleading if not dangerous. It can be hard to tell the difference between a statement which is useful information and one that just looks like it, which combined with misleading advertising is the source of the misconception.&lt;/p&gt;

&lt;p&gt;More than just looking like useful information, there is a sycophantic tendancy inherent in these models. In essence, it’s their tendency to try to please the user by affirming what they say. The models are intentionally designed to be this way, and why wouldn’t they be; the companies building them are simply focusing on whatever they think would make their users the happiest (and thus more likely to continue using the model).&lt;/p&gt;

&lt;p&gt;This is why computer literacy is more important than ever before. The appeal of these models is broad enough to attract a diverse array of users, many of whom do not understand what can happen when they are used extensively. Understanding that these models are not perfect or even very good information compositors and that their primary goal is to please rather than inform is key to keeping yourself safe while using them. Everyone who uses them has a responsibility to educate themselves and those they care about on proper usage and their limits. After all, solving the problem by locking all the computers away in ivory towers and government laboratories would be somewhat impractical.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>A Formalization of the Boole-Schröder Algebra in Coq</title>
   <author><name>Lisanna</name></author>
   <link href="https://lisanna.neocities.org/2025/07/25/a-formalization-of-boole-schroder-algebra-in-coq"/>
   <updated>2025-07-25T01:00:00-04:00</updated>
   <id>https://lisanna.neocities.org/2025/07/25/a-formalization-of-boole-schroder-algebra-in-coq</id>
   <content type="html">&lt;p&gt;As an exercise in learning how to use the Coq proof assistant, I picked up a book on symbolic logic (&lt;a href=&quot;https://archive.org/details/symboliclogic0000lewi_x3c4/mode/2up&quot;&gt;Symbolic Logic: Clarance Irving Lewis &amp;amp; Cooper Harold Langford, 1959&lt;/a&gt;) and decided to try to formalize the Boole-Schröder algebra using it as a guide. I found the book for pennies at &lt;a href=&quot;https://www.secondstorybooks.com/&quot;&gt;Second Story Books&lt;/a&gt; at their Rockville warehouse store (meanwhile the going rate on Amazon for actual copies of the book is hundreds of dollars). It was among a number of old math books on logic and computing I got for cheap.&lt;/p&gt;

&lt;h1 id=&quot;foundations-----------&quot;&gt;Foundations&lt;br /&gt;-----------&lt;/h1&gt;

&lt;p&gt;Boole-Schröder algebra, more commonly known as boolean algebra, was conceived by Ernst Schröder as an extension of George Boole’s Logic. It deals with &lt;em&gt;propositions&lt;/em&gt;, which can represent two different kinds of things: assserting a relation between the &lt;em&gt;concepts&lt;/em&gt; that the terms of an expression &lt;em&gt;connote&lt;/em&gt;, or between the &lt;em&gt;classes&lt;/em&gt; that the terms &lt;em&gt;denote&lt;/em&gt;. The &lt;em&gt;connotation&lt;/em&gt; of terms refers to relationships between &lt;em&gt;concepts&lt;/em&gt;, such as the concept “man” implying the concept “mortal”. The &lt;em&gt;denotation&lt;/em&gt; of terms refers to relationships between &lt;em&gt;classes&lt;/em&gt;, such as the class “man” being contained within the class “mortal”. You can roughly interpret this as the difference between the terms representing statements that are either true or false and sets.&lt;/p&gt;

&lt;p&gt;The algebra is constructed from the following assumptions:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Any ‘term’ (&lt;em&gt;a&lt;/em&gt;, &lt;em&gt;b&lt;/em&gt;, etc.) will represent some class of things, named by some logical term.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;a ⨯ b&lt;/em&gt; (or &lt;em&gt;a b&lt;/em&gt;): what is common to &lt;em&gt;a&lt;/em&gt; and &lt;em&gt;b&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;-a&lt;/em&gt;: not (or inverse of) &lt;em&gt;a&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;0: nothing (null class).&lt;/li&gt;
  &lt;li&gt;&lt;em&gt;a = b&lt;/em&gt; means the terms have the same extension (class).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And the following definitions (book’s numbering system used):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1.01: &lt;em&gt;1 = -0&lt;/em&gt; Def.&lt;/li&gt;
  &lt;li&gt;1.02: &lt;em&gt;a + b = -(-a -b)&lt;/em&gt; Def. Members of &lt;em&gt;a&lt;/em&gt; and members of &lt;em&gt;b&lt;/em&gt; are not things not in neither &lt;em&gt;a&lt;/em&gt; nor &lt;em&gt;b&lt;/em&gt;. They must be in either &lt;em&gt;a&lt;/em&gt; or &lt;em&gt;b&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;1.03: &lt;em&gt;a ⊂ b is the same as a b = a&lt;/em&gt; Def. Class inclusion. Members of &lt;em&gt;a&lt;/em&gt; are members of &lt;em&gt;b&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the following postulates:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1.1: &lt;em&gt;a a = a&lt;/em&gt;. There are no exponents in this algebra.&lt;/li&gt;
  &lt;li&gt;1.2: &lt;em&gt;a b = b a&lt;/em&gt; (commutation).&lt;/li&gt;
  &lt;li&gt;1.3: &lt;em&gt;a (b c) = (a b) c&lt;/em&gt; (association).&lt;/li&gt;
  &lt;li&gt;1.4: &lt;em&gt;a 0 = 0&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;1.5: If &lt;em&gt;a -b = 0&lt;/em&gt;, then &lt;em&gt;a ⊂ b&lt;/em&gt;. If no members of &lt;em&gt;a&lt;/em&gt; are not in &lt;em&gt;b&lt;/em&gt;, then all members of &lt;em&gt;a&lt;/em&gt; are in &lt;em&gt;b&lt;/em&gt;.&lt;/li&gt;
  &lt;li&gt;1.6: If &lt;em&gt;a ⊂ b&lt;/em&gt; and &lt;em&gt;a ⊂ -b&lt;/em&gt;, then &lt;em&gt;a = 0&lt;/em&gt;. Nothing can be in both &lt;em&gt;b&lt;/em&gt; and &lt;em&gt;-b&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I’ve just listed is the entirety of boolean algebra. All further laws can be derived from these assumptions, definitions, and postulates.&lt;/p&gt;

&lt;h1 id=&quot;foundations-in-coq------------------&quot;&gt;Foundations in Coq&lt;br /&gt;------------------&lt;/h1&gt;

&lt;p&gt;We can now construct our BooleSchroderAlgebra Coq class and assign some notation.&lt;/p&gt;

&lt;div class=&quot;language-coq highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;Class&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;BooleSchroderAlgebra&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;zero&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;(* null class *)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;  &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;(* universe of discourse class *)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;union&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;neg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;Prop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  
  &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;(* 1.01 *)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;neg_zero&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;neg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;zero&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;(* 1.01 *)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;neg_one&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;neg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;one&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;(* 1.02 *)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;union_def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;forall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;union&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;neg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;neg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;neg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;(* 1.03 *)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;contains_def&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;forall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  
  &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;(* 1.1 *)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter_identity&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;forall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;(* 1.2 *)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter_comm&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;forall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;(* 1.3 *)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter_assoc&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;forall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;(* 1.4 *)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter_zero&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;forall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;zero&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;(* 1.5 *)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter_neg_zero_contains&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;forall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;neg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;zero&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;c&quot;&gt;(* 1.6 *)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;contains_zero&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kr&quot;&gt;forall&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;/\&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;neg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;zero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;

&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Infix&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;∪&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;union&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;associativity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Infix&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;∩&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;inter&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;associativity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Notation&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;¬ x&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;neg&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;35&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;associativity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;Infix&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;⊂&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:=&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;contains&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;70&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;no&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;no&quot;&gt;associativity&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Our definitions and postulates specified in the Coq class are assumed true by Coq for theorems using terms from that class. Borrowing from set theory, &lt;em&gt;a + b&lt;/em&gt; is represented with the notation &lt;em&gt;a ∪ b&lt;/em&gt;, &lt;em&gt;a b&lt;/em&gt; is represented with the notation &lt;em&gt;a ∩ b&lt;/em&gt;, and &lt;em&gt;-a&lt;/em&gt; is represented with the notation ¬ &lt;em&gt;a&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The type signatures of most of the operations are pretty self-explanatory. However, one stands out in particular: &lt;em&gt;contains&lt;/em&gt;. Its type is given as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A -&amp;gt; A -&amp;gt; Prop&lt;/code&gt;. I stated earlier that this algebra deals with propositions expressed by relations between terms. While all the other operators take terms and yield a term, &lt;em&gt;contains&lt;/em&gt; takes terms and yields a proposition. It’s kind of a meta-proposition, since the terms themselves can be propositions, but not on the same level. Possibly counter-intuitively, it’s improper to write something like &lt;em&gt;a = b ⊂ c&lt;/em&gt;, because it would be nonsensical when the terms &lt;em&gt;denote classes&lt;/em&gt; rather than &lt;em&gt;connote&lt;/em&gt; propositional &lt;em&gt;concepts&lt;/em&gt; (not all &lt;em&gt;concepts&lt;/em&gt; are propositional). The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;=&lt;/code&gt; operator in Coq also has the type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;A -&amp;gt; A -&amp;gt; Prop&lt;/code&gt;, but it is provided for us as a built-in convenience so we don’t need to re-define it.&lt;/p&gt;

&lt;h1 id=&quot;proving-theorems----------------&quot;&gt;Proving Theorems&lt;br /&gt;----------------&lt;/h1&gt;

&lt;p&gt;&lt;img src=&quot;/images/symboliclogic-theorems.jpg&quot; /&gt;&lt;/p&gt;

&lt;p&gt;From here, we can start proving various theorems. So far, I have translated 13 of the book’s proofs into Coq proofs, and I have posted them at &lt;a href=&quot;https://gist.github.com/lisanna-dettwyler/3f26328097c0a7fb8c4fa4e993c5b143&quot;&gt;https://gist.github.com/lisanna-dettwyler/3f26328097c0a7fb8c4fa4e993c5b143&lt;/a&gt;. The theorems as given in the book are pictured above.&lt;/p&gt;

&lt;p&gt;I have found this to be a useful way to practice writing proofs in Coq. Since the proofs in this book tend to be on the terser side, you really have to think through each step carefully to not miss anything, as what can be implied on paper cannot be implied with a machine.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Dynamic Matrix Generation in GitHub Actions</title>
   <author><name>Lisanna</name></author>
   <link href="https://lisanna.neocities.org/2023/08/08/dynamic-matrix-generation-in-github-actions"/>
   <updated>2023-08-08T01:00:00-04:00</updated>
   <id>https://lisanna.neocities.org/2023/08/08/dynamic-matrix-generation-in-github-actions</id>
   <content type="html">&lt;p&gt;An example job with a dynamically-generated matrix conditional on if the workflow is triggered by schedule or a push to a ref starting with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;actions/&lt;/code&gt;.&lt;/p&gt;

&lt;p style=&quot;margin-bottom:0;color:#F0F;&quot;&gt;push-pr-nightly.yml&lt;/p&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;schedule&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;na&quot;&gt;cron&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*&apos;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;branches&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;nv&quot;&gt;master&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;branches&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;nv&quot;&gt;master&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;gen-matrix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;runs-on&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;nv&quot;&gt;ubuntu-latest&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;outputs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ steps.gen.outputs.matrix }}&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;steps&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;na&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gen&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;if ${{ github.event_name == &apos;schedule&apos; || startsWith(github.head_ref, &apos;actions/&apos;) }}; then&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;echo &apos;matrix=[&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;{&quot;os&quot;:&quot;rhel&quot;, &quot;VMAJ&quot;:&quot;8&quot;, &quot;VMIN&quot;:&quot;5&quot;},&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;{&quot;os&quot;:&quot;sles&quot;, &quot;VMAJ&quot;:&quot;15&quot;, &quot;VMIN&quot;:&quot;3&quot;},&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;{&quot;os&quot;:&quot;ubuntu&quot;, &quot;VMAJ&quot;:&quot;18&quot;, &quot;VMIN&quot;:&quot;04&quot;},&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;{&quot;os&quot;:&quot;ubuntu&quot;, &quot;VMAJ&quot;:&quot;20&quot;, &quot;VMIN&quot;:&quot;04&quot;},&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;{&quot;os&quot;:&quot;ubuntu&quot;, &quot;VMAJ&quot;:&quot;22&quot;, &quot;VMIN&quot;:&quot;04&quot;}&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;]&apos; &amp;gt;&amp;gt; ${GITHUB_OUTPUT};&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;echo &apos;matrix=[&lt;/span&gt;
              &lt;span class=&quot;s&quot;&gt;{&quot;os&quot;:&quot;rhel&quot;, &quot;VMAJ&quot;:&quot;8&quot;, &quot;VMIN&quot;:&quot;5&quot;}&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;]&apos; &amp;gt;&amp;gt; ${GITHUB_OUTPUT};&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;fi&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;needs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;gen-matrix&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;runs-on&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;nv&quot;&gt;ubuntu-latest&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;strategy&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;matrix&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ fromJSON(needs.gen-matrix.outputs.matrix) }}&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;steps&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;na&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;echo ${{ matrix.os }} ${{ matrix.VMAJ }}.${{ matrix.VMIN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Use GNU Parallel for Regression Testing</title>
   <author><name>Lisanna</name></author>
   <link href="https://lisanna.neocities.org/2023/07/05/use-gnu-parallel-for-regression-testing"/>
   <updated>2023-07-05T01:00:00-04:00</updated>
   <id>https://lisanna.neocities.org/2023/07/05/use-gnu-parallel-for-regression-testing</id>
   <content type="html">&lt;p&gt;I get a sinking feeling in my stomach when I look at non-parallelized test automation lacking regression analysis. The former can be hard, but the latter isn’t. I’ll show how both are achievable with some simple scripts and the help of GNU parallel, which has recently become one of my favorite tools.&lt;/p&gt;

&lt;p&gt;Regression testing is different from normal testing in that a prior result gets compared with a later result to see if a particular change fixed or broke something. Usually you want to limit the differences to one change at a time so that you know that it was precisely that change which caused the new passes or failures. If you change, for example, the versions of two components, you wouldn’t know which component’s update caused the change in test results.&lt;/p&gt;

&lt;p&gt;Parallelized testing simply refers to running more than one test in parallel so that they complete faster. For tests which do not place heavy load on your system, this can be easy to manage, but I rarely work with tests like this. Working in High-Performance Computing leads to working with workloads and tests that are designed to stress your hardware, making parallelization without crashing your systems difficult.&lt;/p&gt;

&lt;h1 id=&quot;setting-up-our-workloads------------------------&quot;&gt;Setting up our workloads&lt;br /&gt;------------------------&lt;/h1&gt;

&lt;p&gt;For demonstration purposes, we’ll use the example workloads from Intel’s &lt;strong&gt;oneAPI Deep Neural Network Library (oneDNN)&lt;/strong&gt;.&lt;/p&gt;

&lt;p style=&quot;margin-bottom:0;color:#F0F;&quot;&gt;Dockerfile&lt;/p&gt;

&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# syntax=docker/dockerfile:latest&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ARG&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; ONEAPI_VERSION=2023.0.0&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;intel/oneapi-hpckit:${ONEAPI_VERSION}-devel-ubuntu22.04&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;oneapi-hpckit&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;ubuntu:jammy&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;base&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SHELL&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;/bin/bash&quot;, &quot;-e&quot;, &quot;-c&quot;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; CCACHE_DIR=/cache/ccache&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;toolchain&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; DEBIAN_FRONTEND=noninteractive&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--mount&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;cache,target&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/var/apt/cache &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;
rm /etc/apt/apt.conf.d/docker-clean
apt-get update
apt-get install -y \
  build-essential \
  ccache \
  cmake \
  git \
  ninja-build
EOF

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;toolchain&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build-onednn&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --link --from=oneapi-hpckit /opt/intel/oneapi/compiler /opt/intel/oneapi/compiler&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --link --from=oneapi-hpckit /opt/intel/oneapi/tbb /opt/intel/oneapi/tbb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --link --from=oneapi-hpckit /opt/intel/oneapi/setvars.sh /opt/intel/oneapi/setvars.sh&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /onednn-git&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; CCACHE_BASEDIR=/onednn-git&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; CC=icx&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; CXX=icpx&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;--mount&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;cache,target&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/cache/ccache &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;
git clone https://github.com/oneapi-src/oneDNN.git .
git checkout v3.2
source /opt/intel/oneapi/setvars.sh
mkdir build
cd build
cmake \
  -D DNNL_CPU_RUNTIME=DPCPP \
  -D DNNL_GPU_RUNTIME=DPCPP \
  -D CMAKE_ASM_COMPILER=icx \
  -D CMAKE_C_COMPILER_LAUNCHER=ccache \
  -D CMAKE_CXX_COMPILER_LAUNCHER=ccache \
  -D BUILD_SHARED_LIBS=0 \
  -G Ninja \
  ..
ninja -j$(nproc)
EOF

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;toolchain&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;build-parallel&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; https://ftp.gnu.org/gnu/parallel/parallel-20230422.tar.bz2 .&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;
tar xf parallel-20230422.tar.bz2
cd parallel-20230422
./configure --prefix=$PWD/install
make
make install
EOF

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;onednn&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --link --from=build-parallel parallel-20230422/install/bin/* /usr/local/bin/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --link --from=oneapi-hpckit /opt/intel/oneapi/compiler /opt/intel/oneapi/compiler&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --link --from=oneapi-hpckit /opt/intel/oneapi/tbb /opt/intel/oneapi/tbb&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --link --from=oneapi-hpckit /opt/intel/oneapi/setvars.sh /opt/intel/oneapi/setvars.sh&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --link --from=build-onednn /onednn-git/build/src/libdnnl.so* /usr/local/lib/&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --link --from=build-onednn /onednn-git/build/examples /workloads&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; LD_LIBRARY_PATH=/usr/local/lib&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /workloads&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Build this with:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker buildx build &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; localhost/onednn-examples - &amp;lt; Dockerfile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This produces a container image with the oneDNN example workloads. We can execute one of them like this:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; onednn-examples bash &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
source /opt/intel/oneapi/setvars.sh
./getting-started-cpp
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;After a few seconds, this should print the following:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;:: initializing oneAPI environment ...
   bash: BASH_VERSION = 5.1.16(1)-release
   args: Using &quot;$@&quot; for setvars.sh arguments: 
:: compiler -- latest
:: tbb -- latest
:: oneAPI environment initialized ::
 
Example passed on CPU.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;running-in-parallel-------------------&quot;&gt;Running in parallel&lt;br /&gt;-------------------&lt;/h1&gt;

&lt;p&gt;GNU parallel allows us to run the workloads concurrently, taking better advantage of a system’s compute resources.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker run &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--interactive&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(!&lt;/span&gt; docker info 2&amp;gt;/dev/null | &lt;span class=&quot;nb&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-q&lt;/span&gt; rootless&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;then &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;--user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;:&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;:&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;LOG_FILE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;/results.tsv &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  localhost/onednn-examples:latest &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  bash &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
source /opt/intel/oneapi/setvars.sh
memtotal=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;MemTotal /proc/meminfo | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{print $2}&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
echo -e &quot;JobRuntime&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;Exitval&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;Command&quot; &amp;gt; &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/results.tsv
parallel &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
  --will-cite &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
  --color &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
  --tag &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
  --line-buffer &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
  --retries 0 &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
  --delay 1 &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
  --jobs &apos;50%&apos; &lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
  --memfree &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$((&lt;/span&gt;memtotal &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;))&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;K &amp;lt;&amp;lt;EndOfJobs
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./bnorm-u8-via-binary-postops-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./cnn-inference-f32-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./cnn-inference-int8-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./cnn-training-bf16-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./cnn-training-f32-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./cross-engine-reorder-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./getting-started-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./matmul-perf-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./memory-format-propagation-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./performance-profiling-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-augru-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-batch-normalization-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-binary-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-concat-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-convolution-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-eltwise-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-inner-product-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-layer-normalization-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-lrn-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-lstm-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-matmul-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-pooling-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-prelu-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-reduction-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-reorder-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-resampling-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-shuffle-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-softmax-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./primitives-sum-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./rnn-training-f32-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./sycl-interop-buffer-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./sycl-interop-usm-cpp
&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;/ectime ./tutorials-matmul-inference-int8-matmul-cpp
EndOfJobs
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The parallel command is the real magic here.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--color&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--tag&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--line-buffer&lt;/code&gt; control the presentation of each job’s output, giving each job a unique background color, prefixing each line with the name of the job (the workload command), and allowing lines to interleave if one program has output to print before another is done (but not collide with eachother mid-line).&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--jobs &apos;50%&apos;&lt;/code&gt; limits the total percent share of CPU available to launched jobs to 50% of the system’s processors.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--delay 1&lt;/code&gt; helps deal with memory usage spikes by delaying one second between launching subsequent jobs, giving parallel sufficient time to detect and kill jobs when memory usage starts becoming exceeded.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--memfree&lt;/code&gt; limits the number of running jobs.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;memtotal / 4 * 3&lt;/code&gt; means new jobs are not started unless at least 75% of the system’s total memory is free, and if this drops below half of the value (37.5%) then the youngest job will be killed and put back on the queue to be run later.  I have found this to be a good value for the types of workloads I run which can have unpredictable large memory spikes.  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--retries 0&lt;/code&gt; instructs GNU parallel to requeue each job as often as necessary in order for it to eventually succeed.&lt;/p&gt;

&lt;p&gt;Using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--retries 0&lt;/code&gt; has the consequence of not being able to run each workload directly, since failures in addition to requeuing will be retried indefinitely.  This is undesirable for regression testing where we want to catch if any of our jobs are failing, rather than retry them until they succeed.  That is why each job is prefixed with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$PWD/ectime&lt;/code&gt;, which is a small wrapper script which records into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$LOG_FILE&lt;/code&gt; the exit code (“ec”) and runtime (“time”) of each job. It exits with status 0 regardless of whether the command it ran failed or not.&lt;/p&gt;

&lt;p style=&quot;margin-bottom:0;color:#F0F;&quot;&gt;ectime&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#! /usr/bin/env bash&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;LOG_FILE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LOG_FILE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;:-&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PWD&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;/log.tsv&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;start_time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; +%s&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;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;exit_code&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$?&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;end_time&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;date&lt;/span&gt; +%s&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$((&lt;/span&gt;end_time &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; start_time&lt;span class=&quot;k&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;exit_code&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LOG_FILE&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Running this produces a tab-delimited results file with the runtime and exit value of each workload. In this case, they all passed.&lt;/p&gt;

&lt;p style=&quot;margin-bottom:0;color:#F0F;&quot;&gt;results.tsv&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;JobRuntime  Exitval  Command
1           0        ./bnorm-u8-via-binary-postops-cpp
0           0        ./cnn-inference-int8-cpp
0           0        ./cnn-training-bf16-cpp
4           0        ./cnn-inference-f32-cpp
0           0        ./cross-engine-reorder-cpp
1           0        ./cnn-training-f32-cpp
0           0        ./getting-started-cpp
1           0        ./memory-format-propagation-cpp
0           0        ./primitives-augru-cpp
2           0        ./performance-profiling-cpp
0           0        ./primitives-batch-normalization-cpp
0           0        ./primitives-binary-cpp
0           0        ./primitives-concat-cpp
0           0        ./primitives-convolution-cpp
0           0        ./primitives-eltwise-cpp
1           0        ./primitives-inner-product-cpp
0           0        ./primitives-layer-normalization-cpp
0           0        ./primitives-lrn-cpp
0           0        ./primitives-lstm-cpp
0           0        ./primitives-matmul-cpp
0           0        ./primitives-pooling-cpp
0           0        ./primitives-prelu-cpp
0           0        ./primitives-reduction-cpp
17          0        ./matmul-perf-cpp
0           0        ./primitives-reorder-cpp
0           0        ./primitives-resampling-cpp
0           0        ./primitives-shuffle-cpp
0           0        ./primitives-softmax-cpp
0           0        ./primitives-sum-cpp
0           0        ./rnn-training-f32-cpp
0           0        ./sycl-interop-buffer-cpp
0           0        ./sycl-interop-usm-cpp
0           0        ./tutorials-matmul-inference-int8-matmul-cpp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;regression-analysis-------------------&quot;&gt;Regression analysis&lt;br /&gt;-------------------&lt;/h1&gt;

&lt;p&gt;Let’s invent a second run and edit some of the workloads to have failed, for the purposes of demonstrating the regression analysis tool.&lt;/p&gt;

&lt;p style=&quot;margin-bottom:0;color:#F0F;&quot;&gt;results2.tsv&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;JobRuntime  Exitval  Command
1           0        ./bnorm-u8-via-binary-postops-cpp
0           0        ./cnn-inference-int8-cpp
0           0        ./cnn-training-bf16-cpp
4           0        ./cnn-inference-f32-cpp
0           0        ./cross-engine-reorder-cpp
1           0        ./cnn-training-f32-cpp
0           0        ./getting-started-cpp
1           0        ./memory-format-propagation-cpp
0           0        ./primitives-augru-cpp
2           0        ./performance-profiling-cpp
0           0        ./primitives-batch-normalization-cpp
0           1        ./primitives-binary-cpp
0           0        ./primitives-concat-cpp
0           0        ./primitives-convolution-cpp
0           0        ./primitives-eltwise-cpp
1           0        ./primitives-inner-product-cpp
0           0        ./primitives-layer-normalization-cpp
0           0        ./primitives-lrn-cpp
0           0        ./primitives-lstm-cpp
0           1        ./primitives-matmul-cpp
0           1        ./primitives-pooling-cpp
0           0        ./primitives-prelu-cpp
0           0        ./primitives-reduction-cpp
17          0        ./matmul-perf-cpp
0           0        ./primitives-reorder-cpp
0           0        ./primitives-resampling-cpp
0           0        ./primitives-shuffle-cpp
0           0        ./primitives-softmax-cpp
0           0        ./primitives-sum-cpp
0           0        ./rnn-training-f32-cpp
0           0        ./sycl-interop-buffer-cpp
0           0        ./sycl-interop-usm-cpp
0           0        ./tutorials-matmul-inference-int8-matmul-cpp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Using a simple python script, we can print a colorful regression analysis table showing both the “old” and the “new” exit codes for each workload.&lt;/p&gt;

&lt;p style=&quot;margin-bottom:0;color:#F0F;&quot;&gt;compare.py&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;csv&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;argparse&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;termcolor&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cprint&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;argparse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ArgumentParser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prog&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;compare.py&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_argument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;old_log&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argparse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;r&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_argument&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;new_log&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argparse&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FileType&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;r&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parser&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parse_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;old_log&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;old_log&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;new_log&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_log&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;read_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;csv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DictReader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delimiter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\t&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Command&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;row&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Exitval&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;row&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;old&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;read_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;old_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;read_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new_log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;print_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&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;old&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;printer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;old&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;printer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cprint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;black&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;on_red&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attrs&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;s&quot;&gt;&apos;bold&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;old&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;printer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cprint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;black&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;on_green&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attrs&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;s&quot;&gt;&apos;bold&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;old&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;old&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;printer&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;lambda&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cprint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;red&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;attrs&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;s&quot;&gt;&apos;bold&apos;&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;old&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;old&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;printer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;print_table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Old New Command&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;longest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&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;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;-&apos;&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;mi&quot;&gt;15&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;longest&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)))&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&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;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;print_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;print_table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Run this with:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;python3 compare.py results.tsv results2.tsv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This renders the following table with still failing in red, still passing in green, newly failing with a red background, and newly passing with a green background.&lt;/p&gt;

&lt;div style=&quot;padding:1rem&quot;&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;Old New Command&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;----------------------------------------------------&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./bnorm-u8-via-binary-postops-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./cnn-inference-int8-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./cnn-training-bf16-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./cnn-inference-f32-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./cross-engine-reorder-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./cnn-training-f32-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./getting-started-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./memory-format-propagation-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-augru-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./performance-profiling-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-batch-normalization-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:black;background-color:red;font-weight:bold&quot;&gt;  0   1 ./primitives-binary-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-concat-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-convolution-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-eltwise-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-inner-product-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-layer-normalization-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-lrn-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-lstm-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:black;background-color:red;font-weight:bold&quot;&gt;  0   1 ./primitives-matmul-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:black;background-color:red;font-weight:bold&quot;&gt;  0   1 ./primitives-pooling-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-prelu-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-reduction-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./matmul-perf-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-reorder-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-resampling-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-shuffle-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-softmax-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./primitives-sum-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./rnn-training-f32-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./sycl-interop-buffer-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white&quot;&gt;  0   0 ./sycl-interop-usm-cpp&lt;/pre&gt;
&lt;pre style=&quot;margin:0;padding:0;color:white; padding:0;&quot;&gt;  0   0 ./tutorials-matmul-inference-int8-matmul-cpp&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;This achieves our original objective of performing a regression analysis on results from tests that were run in parallel.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Pair Programming with ChatGPT: Bash Retry with STDIN Replay</title>
   <author><name>Lisanna</name></author>
   <link href="https://lisanna.neocities.org/2023/04/07/pair-programming-with-chatgpt-bash-retry-with-stdin-replay"/>
   <updated>2023-04-07T01:00:00-04:00</updated>
   <id>https://lisanna.neocities.org/2023/04/07/pair-programming-with-chatgpt-bash-retry-with-stdin-replay</id>
   <content type="html">&lt;p&gt;ChatGPT can be a very useful programming assistant.  I’ve used it for debugging, documentation reference, and more recently trying out random ideas I think I would find useful.  Its conversational nature and correctibility make it conducive to problem solving.&lt;/p&gt;

&lt;p&gt;Unreliable services outside of your control are disruptive to automation.  Sometimes the best you can do is retry while praying that the problem goes away after a few attempts.  It would be useful to be able to wrap routines that depend on such services in a retry function.&lt;/p&gt;

&lt;p&gt;A basic formulation of the problem is relatively straightforward to implement, but a trickier aspect to handle is retrying commands that accept input on STDIN.  Properly retrying such a command requires capturing and replaying what is received on STDIN.  This added complication made it feel like a fun challenge to try using ChatGPT as a pair-programming assistant.&lt;/p&gt;

&lt;p&gt;Before highlighting the progression of the effort, here is a direct link to the final implementation: &lt;a href=&quot;https://gist.github.com/lisanna-dettwyler/c98072c0683cd9001cea3e9fe7aa885b&quot;&gt;retry.sh&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s the original problem statement I gave it:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Hello! I need a bash script that provides a function called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retry&lt;/code&gt;. Its first argument is the number of times to retry a command, with 0 meaning to retry forever. The remaining arguments are a command and its arguments to retry. The tricky part about this retry function is that I need it to be able to have buffered what it receives on STDIN, such that it can replay it to the command on each retry. For example, if I write &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retry 4 bash -e &amp;lt; &amp;lt;(echo &quot;date &amp;amp;&amp;amp; false&quot;)&lt;/code&gt;, the inner command (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash -e&lt;/code&gt;) will receive the text &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;date &amp;amp;&amp;amp; false&lt;/code&gt; every time it’s retried, even though the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;retry&lt;/code&gt; function only received that text on its STDIN once.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It cheerfully produced the following output:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#!/bin/bash&lt;/span&gt;

retry&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;retries&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;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;shift
  local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cmd&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;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;tmp_input_file
  &lt;span class=&quot;nv&quot;&gt;tmp_input_file&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;nb&quot;&gt;mktemp&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;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tmp_input_file&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

  &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
  &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;exit_code

  &lt;span class=&quot;k&quot;&gt;while&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;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;retries&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-eq&lt;/span&gt; 0 &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;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lt&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;retries&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&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;do&lt;/span&gt;
    &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &amp;lt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tmp_input_file&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;exit_code&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;nv&quot;&gt;$?&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;if&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;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;exit_code&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-eq&lt;/span&gt; 0 &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;then
      &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;break
    &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else
      &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$((&lt;/span&gt;count &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fi
  done

  &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;tmp_input_file&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;exit_code&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I was pleasantly surprised to find that it behaved correctly for the example I provided!&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;➜  ~ retry 4 bash -e &amp;lt; &amp;lt;(echo &quot;date &amp;amp;&amp;amp; false&quot;) 
Wed Apr  5 07:20:24 PM EDT 2023
Wed Apr  5 07:20:24 PM EDT 2023
Wed Apr  5 07:20:24 PM EDT 2023
Wed Apr  5 07:20:24 PM EDT 2023
➜  ~ echo $?
1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;At this point I started wondering about a handful of corner cases, and how many of them it could help me address.  The first one I pursued was not needing to buffer all of STDIN before starting the first attempt.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Is it possible to modify this to not need to wait for all of stdin to be received before running the command?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Its idea was to replace the temporary file with a named pipe fed by a background job, which implemented the behavior I specified.  However, in doing so, it broke the ability for reattempts to receive the same STDIN data as the first attempt because data in named pipes is consumed when read.&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@@ -5,25 +5,28 @@&lt;/span&gt; retry() {
   shift
   local cmd=(&quot;$@&quot;)
 
&lt;span class=&quot;gd&quot;&gt;-  local tmp_input_file
-  tmp_input_file=&quot;$(mktemp)&quot;
-  cat &amp;gt;&quot;${tmp_input_file}&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+  local fifo
+  fifo=&quot;$(mktemp -u)&quot;
+  mkfifo &quot;${fifo}&quot;
+
+  cat &amp;gt;&quot;${fifo}&quot; &amp;amp;
&lt;/span&gt; 
   local count=0
   local exit_code
 
   while [[ &quot;${retries}&quot; -eq 0 || &quot;${count}&quot; -lt &quot;${retries}&quot; ]]; do
&lt;span class=&quot;gd&quot;&gt;-    &quot;${cmd[@]}&quot; &amp;lt;&quot;${tmp_input_file}&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    &quot;${cmd[@]}&quot; &amp;lt;&quot;${fifo}&quot;
&lt;/span&gt;     exit_code=&quot;$?&quot;
 
     if [[ &quot;${exit_code}&quot; -eq 0 ]]; then
       break
     else
       count=$((count + 1))
&lt;span class=&quot;gi&quot;&gt;+      echo &amp;gt;&amp;amp;2 &quot;Retrying... (${count}/${retries})&quot;
&lt;/span&gt;     fi
   done
 
&lt;span class=&quot;gd&quot;&gt;-  rm -f &quot;${tmp_input_file}&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+  rm -f &quot;${fifo}&quot;
&lt;/span&gt; 
   return &quot;${exit_code}&quot;
 }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When I challenged it on this, it acknowledged the error and switched back from a named pipe to a temporary file (retaining the background job to feed it), and used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tail&lt;/code&gt; to feed the contents of the file into the command.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Will this be able to replay everything that was received on stdin? I forgot if a named pipe can be read from multiple times without the data that has been read becoming consumed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@@ -5,28 +5,30 @@&lt;/span&gt; retry() {
   shift
   local cmd=(&quot;$@&quot;)
 
&lt;span class=&quot;gd&quot;&gt;-  local fifo
-  fifo=&quot;$(mktemp -u)&quot;
-  mkfifo &quot;${fifo}&quot;
-
-  cat &amp;gt;&quot;${fifo}&quot; &amp;amp;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+  local tmp_input_file
+  tmp_input_file=&quot;$(mktemp)&quot;
+  
+  # Background process to write stdin to temporary file
+  cat &amp;gt; &quot;${tmp_input_file}&quot; &amp;amp;
&lt;/span&gt; 
   local count=0
   local exit_code
 
   while [[ &quot;${retries}&quot; -eq 0 || &quot;${count}&quot; -lt &quot;${retries}&quot; ]]; do
&lt;span class=&quot;gd&quot;&gt;-    &quot;${cmd[@]}&quot; &amp;lt;&quot;${fifo}&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    # Read from the temporary file and feed it to the command
+    &quot;${cmd[@]}&quot; &amp;lt; &amp;lt;(tail -n +1 -f &quot;${tmp_input_file}&quot;)
&lt;/span&gt;     exit_code=&quot;$?&quot;
 
     if [[ &quot;${exit_code}&quot; -eq 0 ]]; then
       break
     else
       count=$((count + 1))
&lt;span class=&quot;gd&quot;&gt;-      echo &amp;gt;&amp;amp;2 &quot;Retrying... (${count}/${retries})&quot;
&lt;/span&gt;     fi
   done
 
&lt;span class=&quot;gd&quot;&gt;-  rm -f &quot;${fifo}&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+  # Clean up background process and temporary file
+  wait
+  rm -f &quot;${tmp_input_file}&quot;
&lt;/span&gt; 
   return &quot;${exit_code}&quot;
 }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I also asked it about correctly handling interrupts such as Ctrl-C.  It reasonably added a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cleanup&lt;/code&gt; function to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;trap&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Will this clean up the background &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat&lt;/code&gt; process if interrupted with ctrl-c? Will the retry function itself exit on ctrl-c or a termination signal? It should.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@@ -10,6 +10,15 @@&lt;/span&gt; retry() {
 
   # Background process to write stdin to temporary file
   cat &amp;gt; &quot;${tmp_input_file}&quot; &amp;amp;
&lt;span class=&quot;gi&quot;&gt;+  bg_pid=$!
+
+  cleanup() {
+    kill &quot;${bg_pid}&quot; 2&amp;gt;/dev/null
+    rm -f &quot;${tmp_input_file}&quot;
+  }
+
+  # Trap to handle signals and clean up
+  trap cleanup EXIT INT TERM
&lt;/span&gt; 
   local count=0
   local exit_code
&lt;span class=&quot;p&quot;&gt;@@ -27,8 +36,7 @@&lt;/span&gt; retry() {
   done
 
   # Clean up background process and temporary file
&lt;span class=&quot;gd&quot;&gt;-  wait
-  rm -f &quot;${tmp_input_file}&quot;
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+  cleanup
&lt;/span&gt; 
   return &quot;${exit_code}&quot;
 }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;When I tested this version, I found that the script was not exiting when the command succeeds.  I suspected the use of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tail&lt;/code&gt; was responsible for this.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This works when the command to retry fails. When the command succeeds, the script hangs and never exits, which is undesirable. I think this is because the tail command is not terminating. I’m not sure why it terminates when the command fails, but not when the command succeeds.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Its next idea was to switch to using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tail&lt;/code&gt;, and to nest it in a loop to continuously read data from the temporary file.&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@@ -25,14 +25,18 @@&lt;/span&gt; retry() {
 
   while [[ &quot;${retries}&quot; -eq 0 || &quot;${count}&quot; -lt &quot;${retries}&quot; ]]; do
     # Read from the temporary file and feed it to the command
&lt;span class=&quot;gd&quot;&gt;-    &quot;${cmd[@]}&quot; &amp;lt; &amp;lt;(tail -n +1 -f &quot;${tmp_input_file}&quot;)
-    exit_code=&quot;$?&quot;
-
-    if [[ &quot;${exit_code}&quot; -eq 0 ]]; then
-      break
-    else
-      count=$((count + 1))
-    fi
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    while ! &quot;${cmd[@]}&quot; &amp;lt; &amp;lt;(cat &quot;${tmp_input_file}&quot;); do
+      exit_code=&quot;$?&quot;
+
+      if [[ &quot;${exit_code}&quot; -eq 0 ]]; then
+        break 2
+      else
+        count=$((count + 1))
+        if [[ &quot;${retries}&quot; -ne 0 &amp;amp;&amp;amp; &quot;${count}&quot; -ge &quot;${retries}&quot; ]]; then
+          break 2
+        fi
+      fi
+    done
&lt;/span&gt;   done
 
   # Clean up background process and temporary file
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is a confusing change, as the value of having two nested loops is unclear.  I observed that it never exits when the command succeeds, and reports success when a command fails.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This updated version results in strange behavior. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo &quot;date &amp;amp;&amp;amp; false&quot; | retry 4 bash -e&lt;/code&gt; produces one line of output before exiting with status 0, when I expected four lines of output (for four attempts) and an exit status of 1. When I run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;echo &quot;date &amp;amp;&amp;amp; true&quot; | retry 4 bash -e&lt;/code&gt;, it retries indefinitely and never returns.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next attempt was to revert back to using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tail&lt;/code&gt;, and depends on the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cleanup&lt;/code&gt; function killing the background &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat&lt;/code&gt; process, which is supposed to cause &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tail&lt;/code&gt; to exit.&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@@ -25,18 +25,14 @@&lt;/span&gt; retry() {
 
   while [[ &quot;${retries}&quot; -eq 0 || &quot;${count}&quot; -lt &quot;${retries}&quot; ]]; do
     # Read from the temporary file and feed it to the command
&lt;span class=&quot;gd&quot;&gt;-    while ! &quot;${cmd[@]}&quot; &amp;lt; &amp;lt;(cat &quot;${tmp_input_file}&quot;); do
-      exit_code=&quot;$?&quot;
-
-      if [[ &quot;${exit_code}&quot; -eq 0 ]]; then
-        break 2
-      else
-        count=$((count + 1))
-        if [[ &quot;${retries}&quot; -ne 0 &amp;amp;&amp;amp; &quot;${count}&quot; -ge &quot;${retries}&quot; ]]; then
-          break 2
-        fi
-      fi
-    done
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    &quot;${cmd[@]}&quot; &amp;lt; &amp;lt;(tail -n +1 -f &quot;${tmp_input_file}&quot;)
+    exit_code=&quot;$?&quot;
+
+    if [[ &quot;${exit_code}&quot; -eq 0 ]]; then
+      break
+    else
+      count=$((count + 1))
+    fi
&lt;/span&gt;   done
 
   # Clean up background process and temporary file
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This change also makes no sense, because the command blocking ensures that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cleanup&lt;/code&gt; never runs, thus never killing the background &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat&lt;/code&gt; process.  I decided that I needed to help it out a bit, and proposed that the background process stream STDIN to the command line-by-line through a named pipe, exiting if it detects that the command running in the background has exited.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;false&lt;/code&gt; test works correctly, but the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;true&lt;/code&gt; test hangs after printing the expected one line of output. It seems like the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tail&lt;/code&gt; command is still not terminating, preventing progression of the script from reaching the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cleanup&lt;/code&gt; function that would kill the background &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cat&lt;/code&gt; command. Instead of using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tail&lt;/code&gt;, another function may need to be introduced that reads from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmp_input_file&lt;/code&gt;, checking if the command receiving input has terminated or not in between each line read and sent. Maybe a named pipe could be used by the function that reads from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmp_input_file&lt;/code&gt; to stream stdin to the command? I’m not sure if this would work though, or if it’s the best approach.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The implementation it generated served as the starting point for my own manual iterations on the program.&lt;/p&gt;

&lt;div class=&quot;language-diff highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;@@ -1,5 +1,20 @@&lt;/span&gt;
 #!/bin/bash
 
&lt;span class=&quot;gi&quot;&gt;+stream_input() {
+  local tmp_input_file=&quot;$1&quot;
+  local cmd_pid=&quot;$2&quot;
+  local fifo=&quot;$3&quot;
+
+  while kill -0 &quot;${cmd_pid}&quot; 2&amp;gt;/dev/null; do
+    while read -r line; do
+      echo &quot;${line}&quot; &amp;gt; &quot;${fifo}&quot;
+      if ! kill -0 &quot;${cmd_pid}&quot; 2&amp;gt;/dev/null; then
+        break
+      fi
+    done &amp;lt; &quot;${tmp_input_file}&quot;
+  done
+}
+
&lt;/span&gt; retry() {
   local retries=&quot;$1&quot;
   shift
&lt;span class=&quot;p&quot;&gt;@@ -12,9 +27,14 @@&lt;/span&gt; retry() {
   cat &amp;gt; &quot;${tmp_input_file}&quot; &amp;amp;
   bg_pid=$!
 
&lt;span class=&quot;gi&quot;&gt;+  local fifo
+  fifo=&quot;$(mktemp -u)&quot;
+  mkfifo &quot;${fifo}&quot;
+
&lt;/span&gt;   cleanup() {
     kill &quot;${bg_pid}&quot; 2&amp;gt;/dev/null
     rm -f &quot;${tmp_input_file}&quot;
&lt;span class=&quot;gi&quot;&gt;+    rm -f &quot;${fifo}&quot;
&lt;/span&gt;   }
 
   # Trap to handle signals and clean up
&lt;span class=&quot;p&quot;&gt;@@ -24,8 +44,15 @@&lt;/span&gt; retry() {
   local exit_code
 
   while [[ &quot;${retries}&quot; -eq 0 || &quot;${count}&quot; -lt &quot;${retries}&quot; ]]; do
&lt;span class=&quot;gd&quot;&gt;-    # Read from the temporary file and feed it to the command
-    &quot;${cmd[@]}&quot; &amp;lt; &amp;lt;(tail -n +1 -f &quot;${tmp_input_file}&quot;)
&lt;/span&gt;&lt;span class=&quot;gi&quot;&gt;+    # Start the command
+    &quot;${cmd[@]}&quot; &amp;lt; &quot;${fifo}&quot; &amp;amp;
+    cmd_pid=$!
+
+    # Stream input to the command
+    stream_input &quot;${tmp_input_file}&quot; &quot;${cmd_pid}&quot; &quot;${fifo}&quot; &amp;amp;
+
+    # Wait for the command to finish
+    wait &quot;${cmd_pid}&quot;
&lt;/span&gt;     exit_code=&quot;$?&quot;
 
     if [[ &quot;${exit_code}&quot; -eq 0 ]]; then
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It was at this point that I decided I had reached the point of diminishing returns, wherein the program was becoming more complex than it was capable of usefully understanding.  The fact that even after pointing it in a solid direction it generated a plainly broken implementation was good evidence of this (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;stream_input&lt;/code&gt; will repeatedly dump the contents of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tmp_input_file&lt;/code&gt; into the named pipe).&lt;/p&gt;

&lt;p&gt;The final implementation ended up being significantly more complicated than I was originally expecting this problem to require.  It is included below with explanations.&lt;/p&gt;

&lt;p style=&quot;margin-bottom:0;color:#F0F;&quot;&gt;retry.sh&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#! /usr/bin/env bash&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# usage: retry ATTEMPTS COMMAND [...]&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# This script provides a retry function capable of capturing and replaying data&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# from stdin to the command being retried, so that each attempt receives the&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# input.  The data from stdin is streamed to the command immediately, it is not&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# pre-buffered, and thus will not lock up if the input from stdin never ends.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# If ATTEMPTS is 0, COMMAND is retried forever until it succeeds.&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Suppress some of the job control messages.  Unfortunately it is impossible to&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# suppress all of them.&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt; +m

retry&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;retries&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;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;shift
    local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cmd&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;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;# Background process which reads stdin data from stdin_reader through&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# in_fifo, buffers it in replay_file, and writes it to the stdin of each&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# command attempt through out_fifo.&lt;/span&gt;
    stream_input&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cmd_pid&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;nv&quot;&gt;$1&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;stdin_reader_pid&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;nv&quot;&gt;$2&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in_fifo&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;nv&quot;&gt;$3&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;out_fifo&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;nv&quot;&gt;$4&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;replay_file&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;nv&quot;&gt;$5&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

        &lt;span class=&quot;c&quot;&gt;# Hold out_fifo open for writing.  The command might otherwise exit&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# prematurely if the pipe is closed by a bash I/O redirect operator in&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# between individual line writes.&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;6&amp;gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;out_fifo&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; line&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;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&amp;amp;6
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-0&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cmd_pid&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
                &lt;span class=&quot;c&quot;&gt;# Stop replaying from replay_file to out_fifo if the command&lt;/span&gt;
                &lt;span class=&quot;c&quot;&gt;# has exited.&lt;/span&gt;
                &lt;span class=&quot;nb&quot;&gt;break
            &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fi
        done&lt;/span&gt; &amp;lt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;replay_file&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

        &lt;span class=&quot;c&quot;&gt;# This is used to signal if data is done being sent over in_fifo by&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# stdin_reader.&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;final_read&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;false

        exec &lt;/span&gt;5&amp;lt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in_fifo&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;while &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do
            if &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt; 0 &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; 5&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
                &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&lt;/span&gt; 5 line
                &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;replay_file&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
                &lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;line&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&amp;amp;6
            &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;final_read&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then
                &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;break
            &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fi

            if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-0&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cmd_pid&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
                &lt;span class=&quot;c&quot;&gt;# Stop passing lines from in_fifo to out_fifo if the command&lt;/span&gt;
                &lt;span class=&quot;c&quot;&gt;# has exited.&lt;/span&gt;
                &lt;span class=&quot;nb&quot;&gt;break
            &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-0&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;stdin_reader_pid&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;then&lt;/span&gt;
                &lt;span class=&quot;c&quot;&gt;# Since stdin_reader has exited, nothing new will come over&lt;/span&gt;
                &lt;span class=&quot;c&quot;&gt;# in_fifo.  Signal the next iteration of the loop to finish&lt;/span&gt;
                &lt;span class=&quot;c&quot;&gt;# reading whatever remains in the pipe and exit.&lt;/span&gt;
                &lt;span class=&quot;nv&quot;&gt;final_read&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true
            &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fi
        done&lt;/span&gt;

        &lt;span class=&quot;c&quot;&gt;# Close out_fifo and in_fifo.&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;5&amp;lt;&amp;amp;-
        &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;6&amp;gt;&amp;amp;-
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;# Create a named pipe to receive data from stdin asynchronously. Bind the&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# pipe to &amp;amp;3 to keep it open on both ends.  It needs to be held open for&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# writing because subsequent instances of stream_input will block on&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# opening it for reading if stdin_reader has exited, which would otherwise&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# have been the only thing holding it open.  It needs to be held open for&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# reading to prevent holding it open it for writing from blocking.&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;in_fifo
    &lt;span class=&quot;nv&quot;&gt;in_fifo&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;nb&quot;&gt;mktemp&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&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;nb&quot;&gt;mkfifo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in_fifo&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;exec &lt;/span&gt;3&amp;lt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in_fifo&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in_fifo&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &amp;amp;
    &lt;span class=&quot;nv&quot;&gt;stdin_reader_pid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$!&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;# The replay file buffers data received on stdin so that it can be passed&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# to reattempts of the command.&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;replay_file
    &lt;span class=&quot;nv&quot;&gt;replay_file&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;nb&quot;&gt;mktemp&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

    cleanup&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# Kill all background jobs&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# https://unix.stackexchange.com/a/609300&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;j &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;jobs&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;awk&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;{gsub(&quot;[^0-9]&quot;,&quot;&quot;,$1);printf &quot;%%%s\n&quot;, $1}&apos;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&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;nb&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$j&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in_fifo&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in_fifo&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;replay_file&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;replay_file&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;[[&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;out_fifo&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;out_fifo&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;wait&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;nb&quot;&gt;trap &lt;/span&gt;cleanup EXIT INT TERM

    &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;0
    &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;exit_code

    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;retries &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; 0 &lt;span class=&quot;o&quot;&gt;||&lt;/span&gt; count &amp;lt; retries&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;do&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# Create a named pipe to receive buffered and replayed data from stdin&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# from stream_input.&lt;/span&gt;
        &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;out_fifo
        &lt;span class=&quot;nv&quot;&gt;out_fifo&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;nb&quot;&gt;mktemp&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-u&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;nb&quot;&gt;mkfifo&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;out_fifo&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

        &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[@]&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &amp;lt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;out_fifo&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &amp;amp;
        &lt;span class=&quot;nv&quot;&gt;cmd_pid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$!&lt;/span&gt;

        stream_input &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cmd_pid&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;stdin_reader_pid&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;in_fifo&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;out_fifo&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
          &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;replay_file&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt; &amp;amp;
        &lt;span class=&quot;nv&quot;&gt;stream_input_pid&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$!&lt;/span&gt;

        &lt;span class=&quot;nb&quot;&gt;wait&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;cmd_pid&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
        &lt;span class=&quot;nv&quot;&gt;exit_code&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;nv&quot;&gt;$?&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

        &lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;out_fifo&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;exit_code &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; 0&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;then
            &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;break
        &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;else
            &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;$((&lt;/span&gt;count &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;fi
    done

    &lt;/span&gt;cleanup

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;exit_code&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now that I have a functional implementation, it is easy to see in hindsight that bash was likely not the best choice for this.  It’s if nothing else an interesting example of the types of limits you can run into when using ChatGPT for programming problem-solving.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>You&apos;re Not Crazy: Dockerfile Build Caching for ADD/COPY is Broken</title>
   <author><name>Lisanna</name></author>
   <link href="https://lisanna.neocities.org/2023/03/20/youre-not-crazy-dockerfile-build-caching-for-add-copy-is-broken"/>
   <updated>2023-03-20T01:00:00-04:00</updated>
   <id>https://lisanna.neocities.org/2023/03/20/youre-not-crazy-dockerfile-build-caching-for-add-copy-is-broken</id>
   <content type="html">&lt;p&gt;Consider this workflow.  You have created a git repository on your local machine containing a Dockerfile and a file it copies into the resulting image.&lt;/p&gt;

&lt;p style=&quot;margin-bottom:0;color:#F0F;&quot;&gt;Dockerfile&lt;/p&gt;

&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# syntax=docker/dockerfile:1.5&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; debian:bullseye&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; cat-me cat-me&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat &lt;/span&gt;cat-me
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p style=&quot;margin-bottom:0;color:#F0F;&quot;&gt;cat-me&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mow!
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You also add a simple GitHub Action that builds and pushes a cache of this image to a registry, so that developers checking out the repository and running the build do not need to waste time rebuilding parts that have already been built.&lt;/p&gt;

&lt;p style=&quot;margin-bottom:0;color:#F0F;&quot;&gt;.github/workflows/build.yml&lt;/p&gt;

&lt;div class=&quot;language-yml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;branches&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;nv&quot;&gt;master&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;steps&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;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;actions/checkout@v3&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker/login-action@v2&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;registry&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ghcr.io&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ github.actor }}&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;docker/setup-buildx-action@v2&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;run&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;docker buildx build \&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;--pull \&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;--cache-to type=inline \&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;--cache-from type=registry,ref=ghcr.io/lynxqtel/cat:latest \&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;--push \&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;-t ghcr.io/lynxqtel/cat:latest \&lt;/span&gt;
          &lt;span class=&quot;s&quot;&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This uses buildkit to build and push an image with its cache inlined, which allows incremental step results to be reused in subsequent builds with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--cache-from&lt;/code&gt;, both in the Actions workflow and on developers’ machines.  Developers who have cloned the repository can leverage this automation to produce builds that can be downloaded as cache without needing to run the builds on their own machine.  This can be extremely useful if there are multiple different variants of the image that could be built, the automation machines are significantly more powerful or parallelizable than the developers’ machine, or if the builds just take a very long time.&lt;/p&gt;

&lt;p&gt;The above is a fanciful idea that is not true.  The setup as described will not always produce cache hits when a developer runs a comparable build command.&lt;/p&gt;

&lt;h3 id=&quot;user-groups-and-umask&quot;&gt;User Groups and UMASK&lt;/h3&gt;

&lt;p&gt;A process’ umask controls the permissions for new files.  &lt;a href=&quot;https://serverfault.com/questions/1126370/i-cannot-find-where-0002-umask-is-being-set-on-ubuntu-20-04&quot;&gt;On Ubuntu systems&lt;/a&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/etc/login.defs&lt;/code&gt; provides its default definition and attributes as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;UMASK 022&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;USERGROUPS_ENAB yes&lt;/code&gt;.  The value of the umask masks (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NAND&lt;/code&gt;s) which bits of the user, group, and other permission fields can be set. The file provides the following commentary on these settings:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# UMASK is the default umask value for pam_umask and is used by&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# useradd and newusers to set the mode of the new home directories.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 022 is the &quot;historical&quot; value in Debian for UMASK&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 027, or even 077, could be considered better for privacy&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# There is no One True Answer here : each sysadmin must make up his/her&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# mind.&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# If USERGROUPS_ENAB is set to &quot;yes&quot;, that will modify this UMASK default value&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# for private user groups, i. e. the uid is the same as gid, and username is&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# the same as the primary group name: for these, the user permissions will be&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# used as group permissions, e. g. 022 will become 002.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;What this means is for a developer cloning a repository under an account with a GID matching their UID (private user group), the permissions of all the files in the checkout will be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-rw-rw--r--&lt;/code&gt; due to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;USERGROUPS_ENAB&lt;/code&gt; setting altering the umask from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;022&lt;/code&gt; to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;002&lt;/code&gt;.  If the developer’s account GID does not match their UID, the umask is left unmodified at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;022&lt;/code&gt;, resulting in permissions of the checked-out files to be &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-rw-r---r--&lt;/code&gt;.  This means that the group of an account being a private user group or not affects permissions for new files.&lt;/p&gt;

&lt;h3 id=&quot;addcopy-caching&quot;&gt;ADD/COPY Caching&lt;/h3&gt;

&lt;p&gt;Both the Docker daemon and the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker-container&lt;/code&gt; buildx driver builders compute a checksum of the file(s) in the build context specified in an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ADD&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY&lt;/code&gt; instruction.  The files’ permissions are included in this checksum, and it is currently &lt;a href=&quot;https://github.com/docker/buildx/issues/1311&quot;&gt;not possible&lt;/a&gt; to prevent different permissions from producing different checksums.  Even when using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--chmod&lt;/code&gt; option, this only applies to the file after the checksum is computed, rather than before.&lt;/p&gt;

&lt;p&gt;It is in this way that having a different effective umask between two checkouts will cause ADD/COPY instructions to produce different checksums, which can cause cache misses depending on its value on the machine that built and pushed the cache.&lt;/p&gt;

&lt;h3 id=&quot;solutions&quot;&gt;Solutions&lt;/h3&gt;

&lt;p&gt;There are no elegant ways to deal with this.  It can either be addressed on the user side or the producer side.  A user could use a script to manually set permissions of files in a Docker build context before performing a build, such as with a git checkout hook.  Alternately, the producer can publish multiple cache builds of the same image for each variant of umask expected to be relevant across all the developers.  In this case, additional &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--cache-from&lt;/code&gt; flags must be provided on the developer’s build command for each variant.  While the latter approach avoids placing a burden on other developers to only run build command-lines as part of or adjacent to permission-fixing shell scripts, it will add additional complexity to what may already be a complciated tagging scheme.  I slightly prefer the former method as straightforward setup instructions for a git checkout hook that unifies the permissions of the entire Docker build context will effectively solve the problem without any additional action or accommodation needed.&lt;/p&gt;

&lt;p&gt;Until an official solution to this problem is developed and merged, developers performing Docker image builds should take care to check that the permissions on the file they wish to include in the build match the permissions used when creating caches.  Git does not track file permissions, so even with a completely clean repository different developers can get different cache hit success rates simply due to whether or not their GID matches their UID, and in the process of trying to discover this come dangerously close to descending into madness.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Beyond the Sidewalk&apos;s End: The Internet without Section 230</title>
   <author><name>Lisanna</name></author>
   <link href="https://lisanna.neocities.org/2023/02/25/beyond-the-sidewalks-end-the-internet-without-section-230"/>
   <updated>2023-02-25T00:00:00-05:00</updated>
   <id>https://lisanna.neocities.org/2023/02/25/beyond-the-sidewalks-end-the-internet-without-section-230</id>
   <content type="html">&lt;blockquote&gt;
  &lt;p&gt;(1) No provider or user of an interactive computer service shall be treated as the publisher or speaker of any information provided by another information content provider.&lt;/p&gt;

  &lt;p&gt;(2) No provider or user of an interactive computer service shall be held liable on account of any action voluntarily taken in good faith to restrict access to or availability of material that the provider or user considers to be obscene, lewd, lascivious, filthy, excessively violent, harassing, or otherwise objectionable, whether or not such material is constitutionally protected; or any action taken to enable or make available to information content providers or others the technical means to restrict access to material described in paragraph (1).  (47 U.S.C. § 230(c))&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;With the Supreme Court set to rule on Google’s liability for algorithmically-promoted terrorism content under &lt;a href=&quot;https://www.eff.org/issues/cda230&quot;&gt;Section 230&lt;/a&gt; (&lt;a href=&quot;https://archive.is/vl67y&quot;&gt;archive&lt;/a&gt;) of the Communications Decency Act, I began imagining what may become of the Internet without its protections.  Occasionally during moments of exasperation and frustration when I see the horrors wrought when &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_most-followed_TikTok_accounts&quot;&gt;commoners&lt;/a&gt; (&lt;a href=&quot;https://archive.is/Fr6rz&quot;&gt;archive&lt;/a&gt;) are allowed access to computers with an Internet connection, I fantasize about an alternate reality.  In this admittedly elistist fantasy, computers remained primarily tools of education, science and engineering, and the Internet looks more like it did before a small handful of websites dominated a majority of the traffic and content generation: a diverse collection of smaller, individual websites and semi-isolated networks with the creative control solely in the hands of the operators and direct members of the surrounding communities.  The technical burden of publishing content on the Internet fell mostly to the individual creating it, rather than publishing platforms like YouTube and Facebook that do most of the technical work for you, at the cost of needing to abide by their rules and to be subject to their moderating discretion.&lt;/p&gt;

&lt;p&gt;In short: you had to &lt;a href=&quot;https://www.usenix.org/system/files/1311_05-08_mickens.pdf&quot;&gt;know what you were doing&lt;/a&gt; (&lt;a href=&quot;https://archive.ph/ufWZh&quot;&gt;archive&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;With the survival of Section 230’s protections uncertain, a return to these norms may no longer be out of the question.&lt;/p&gt;

&lt;h1 id=&quot;without-recommendation-engines------------------------------&quot;&gt;Without Recommendation Engines&lt;br /&gt;------------------------------&lt;/h1&gt;

&lt;p&gt;If providers can be held liable for consequences of user exposure to content promoted by recommendation engines (“the algorithm,”) the sophistication of these mechanisms may become significantly hobbled, with them perhaps even being done away with altogether.  Content on social media platforms may be presented only in chronological order.  Questions would arise about what constitutes a recommendation engine; are search engines just a type of recommendation engine?  Certain types of platforms like YouTube could become extremely difficult to use without users possessing foreknowledge of what specific content they want to access.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Section 230 has always protected the ability of online services and their users to highlight relevant, useful content from others, connecting people to the material they enjoy. Legal risk for recommending or organizing content would reduce useful services like showing the best job listings, listing the most relevant products, or displaying the most helpful videos of recipes, songs, or sources of news, entertainment and information.&lt;/p&gt;

  &lt;p&gt;——Google, &lt;a href=&quot;https://blog.google/outreach-initiatives/public-policy/gonzalez-v-google-and-the-future-of-an-open-free-and-safe-internet/&quot;&gt;Gonzalez v Google and the future of an open, free and safe internet&lt;/a&gt; (&lt;a href=&quot;https://archive.ph/qBeEF&quot;&gt;archive&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think Google’s assessment of the possible outcomes of a ruling against them is reasonable.  Even moreso, such a ruling could precede a wave of new lawsuits that may seek to further chip away at Section 230’s afforded protections.  The following is speculation on how the topology of what we currently know as the Internet may change in response.&lt;/p&gt;

&lt;h1 id=&quot;everything-or-nothing---------------------&quot;&gt;Everything or Nothing&lt;br /&gt;---------------------&lt;/h1&gt;

&lt;p&gt;If platforms can no longer afford the risk of permitting their users from publishing their own content without either the strictest of moderation or a complete lack of moderation, they will split into several distinct types: unmoderated badlands where only illegal content is removed, private websites and networks, and &lt;a href=&quot;https://clubpenguin.fandom.com/wiki/Ultimate_Safe_Chat&quot;&gt;Club Penguin&lt;/a&gt; (&lt;a href=&quot;https://archive.ph/rofTu&quot;&gt;archive&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;As documented in &lt;a href=&quot;https://www.jeffkosseff.com/home&quot;&gt;The Twenty-Six Words that Created the Internet&lt;/a&gt; (&lt;a href=&quot;https://archive.ph/YgGrv&quot;&gt;archive&lt;/a&gt;), a 1995 ruling by a New York state court judge against the ISP Prodigy established a precedent that a computer service “provider” can be considered liable as a “publisher” if it exercises &lt;em&gt;any&lt;/em&gt; editorial control of the content it hosts, even when submitted by its users.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;By actively utilizing technology and manpower to delete notes from its computer bulletin boards on the basis of offensiveness and “bad taste”, for example, PRODIGY is clearly making decisions as to content (see, Miami Herald Publishing Co. v. Tornillo, supra ), and such decisions constitute editorial control….PRODIGY’s conscious choice, to gain the benefits of editorial control, has opened it up to a greater liability than CompuServe and other computer networks that make no such choice.&lt;/p&gt;

  &lt;p&gt;——Stratton Oakmont, Inc. v. Prodigy Services Co., &lt;a href=&quot;https://h2o.law.harvard.edu/cases/4540&quot;&gt;23 Media L. Rep. 1794&lt;/a&gt; (N.Y. Sup. Ct. 1995) (&lt;a href=&quot;https://archive.ph/CPE72&quot;&gt;archive&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If Section 230’s protections become undone, it is reasonable to predict that this precendent will again become relevant and applicable.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Section 230 supports free expression online, enabling websites to offer a wide-range of information and allow a diversity of voices to be discovered and heard. Without Section 230, some websites would be forced to overblock, filtering content that could create any potential legal risk, and might shut down some services altogether. That would leave consumers with less choice to engage on the internet and less opportunity to work, play, learn, shop, create, and participate in the exchange of ideas online.&lt;/p&gt;

  &lt;p&gt;——Google, &lt;a href=&quot;https://blog.google/outreach-initiatives/public-policy/gonzalez-v-google-and-the-future-of-an-open-free-and-safe-internet/&quot;&gt;Gonzalez v Google and the future of an open, free and safe internet&lt;/a&gt; (&lt;a href=&quot;https://archive.ph/qBeEF&quot;&gt;archive&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1 id=&quot;survival--------&quot;&gt;Survival&lt;br /&gt;--------&lt;/h1&gt;

&lt;p&gt;Without the assistance of major platforms to publish and promote your content, each website and Internet service effectively becomes its own publisher, of its own operator’s content, unless you opt to publish on a platform alongside offensive content.  Websites will diversify, and so will the infrastructure hosting and connecting them, becoming tightly coupled with each other.  Special interest communities will form around constructing and sharing common infrastructure, and only members of those communities will be able to publish on it.&lt;/p&gt;

&lt;p&gt;Even worse, connectivity itself may become balkanized.  The potential liability of serving problematic content will see not just the hosting infrastructure becoming highly localized but the connectivity infrastructure as well, requiring you to be both a member of the relevant communities to even access their content and sufficiently geographically adjacent to an access point, as peering between these communities will be limited.&lt;/p&gt;

&lt;p&gt;An extremely prescient example of this has recently taken place, with the attempted (and ultimately failed) removal of the &lt;a href=&quot;https://kiwifarms.net/&quot;&gt;Kiwi Farms&lt;/a&gt; (&lt;a href=&quot;https://archive.is/jyFIc&quot;&gt;archive&lt;/a&gt;) from the Internet.  A long-running DDoS attack has been targeting the site for months.  It has suffered frequent outages as a result, and its owner Joshua Moon has struggled (but succeeded) in keeping it online due to pressure on infrastructure providers to discontinue not just DDoS protection but also hosting and even layer 3 connectivity.  Tier 1 providers such as CentryLink and Arelion blackholed the site’s IP address range; &lt;strong&gt;&lt;em&gt;access to the site was occasionally dependent on who your ISP was&lt;/em&gt;&lt;/strong&gt;.  None of this was done at the request or order of any legal proceeding or judgement, it was exclusively private companies providing &lt;a href=&quot;https://protectthestack.org/&quot;&gt;important infrastructure&lt;/a&gt; (&lt;a href=&quot;https://archive.ph/bxBAr&quot;&gt;archive&lt;/a&gt;) exercising their right to moderate the content they serve.  Section 230 also protects this, as does the United States’ lack of net neutrality rules.&lt;/p&gt;

&lt;p&gt;I consider Joshua’s perspective on this valuable because of the unique challenges he has faced with keeping his site reachable from the clearnet.  Irrespective of any deserved condemnation of the legally-hosted content of this particular site and its operator, the normalization of lower-level infrastructure providers engaging in content moderation will inevitably affect the Internet’s topology.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The Internet is also very fragile. It has many moving parts and drives the politics of our world today. For this reason, it will die, and it will die very soon. Our big-I Internet is being broken apart and will soon become many little-i internets. Every large country or trade union will have its own local and strictly regulated internet. Connections between internets will occur on the vestigial remains of the big-I Internet, requiring a special business permits to access.&lt;/p&gt;

  &lt;p&gt;——Joshua Moon, &lt;a href=&quot;https://madattheinternet.substack.com/p/where-the-sidewalk-ends-the-death&quot;&gt;Where the Sidewalk Ends: The Death of the Internet&lt;/a&gt; (&lt;a href=&quot;https://archive.ph/G26tW&quot;&gt;archive&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I believe the topology fragmentation can become even more extreme than this.  It’s not so unreasonable to speculate that fear of legal liability could produce similar results as does &lt;a href=&quot;https://blog.cloudflare.com/kiwifarms-blocked/&quot;&gt;fear of negative PR and business loss&lt;/a&gt; (&lt;a href=&quot;https://archive.ph/X40H8&quot;&gt;archive&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;I may yet see my strange regressive fantasy come to pass.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Avoid CMD and PowerShell with a Visual Studio Dockerfile with Bash support</title>
   <author><name>Lisanna</name></author>
   <link href="https://lisanna.neocities.org/2023/02/03/avoid-cmd-and-powershell-with-a-visual-studio-dockerfile-with-bash-support"/>
   <updated>2023-02-03T20:50:00-05:00</updated>
   <id>https://lisanna.neocities.org/2023/02/03/avoid-cmd-and-powershell-with-a-visual-studio-dockerfile-with-bash-support</id>
   <content type="html">&lt;p&gt;Automation for projects targeting different operating systems is sometimes written twice: once in a Unix shell scripting language (normally bash) for Linux, BSD, and macOS, and once in either cmd or PowerShell for Windows. Most of the logic in these scripts will be equivalent, and therefore duplicated. Duplicated code is more difficult and error-prone to maintain when making updates.&lt;/p&gt;

&lt;p&gt;However, there are a handful of distributions of Bash for Windows, which let you write (nearly) identical scripts across all targeted operating systems. GitHub Actions makes this easy by making these &lt;a href=&quot;https://github.com/actions/runner-images/blob/main/images/win/Windows2019-Readme.md#shells&quot;&gt;distributions&lt;/a&gt; of Bash available on the Windows runners.&lt;/p&gt;

&lt;p&gt;I’ve created a Dockerfile that can serve as a base image for building software on Windows using Bash, both within a derivative image and executing as a development container. It is derived from an &lt;a href=&quot;https://learn.microsoft.com/en-us/visualstudio/install/build-tools-container&quot;&gt;example&lt;/a&gt; from Microsoft. It installs the specified version of the Visual Studio Build Tools, the &lt;a href=&quot;https://chocolatey.org/&quot;&gt;Chocolatey&lt;/a&gt; package manager, &lt;a href=&quot;https://ccache.dev/&quot;&gt;Ccache&lt;/a&gt;, 7zip, and Git (which includes a distribution of Bash when installed with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/GitAndUnixToolsOnPath&lt;/code&gt; parameter). It also sets the entrypoint to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;...\vcvars64.bat &amp;amp;&amp;amp;&lt;/code&gt; which ensures any command executed as part of a container run will do so with the 64-bit build tools available, including &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cmake.exe&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cl.exe&lt;/code&gt;.&lt;/p&gt;

&lt;p style=&quot;margin-bottom:0;color:#F0F;&quot;&gt;windows-toolchain.Dockerfile&lt;/p&gt;

&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# escape=`&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; mcr.microsoft.com/windows/servercore:ltsc2019&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SHELL&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;powershell&quot;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;New-ItemProperty &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
  -Path &quot;HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem&quot; `
  -Name &quot;LongPathsEnabled&quot; `
  -Value 1 `
  -PropertyType DWORD `
  -Force
&lt;span class=&quot;k&quot;&gt;ARG&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; VS_VERSION=2019&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; VS_VERSION=${VS_VERSION}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
[System.Net.ServicePointManager]::SecurityProtocol = `
  [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; `
Switch ($env:VS_VERSION) { `
    &quot;2019&quot; {$url_version = &quot;16&quot;} `
    &quot;2022&quot; {$url_version = &quot;17&quot;} `
    default {echo &quot;Unsupported VS version $env:VS_VERSION&quot;; EXIT 1} `
}; `
wget `
  -Uri https://aka.ms/vs/${url_version}/release/vs_buildtools.exe `
  -OutFile vs_buildtools.exe

&lt;span class=&quot;k&quot;&gt;SHELL&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;cmd&quot;, &quot;/S&quot;, &quot;/C&quot;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; VS=&quot;C:\Program Files (x86)\Microsoft Visual Studio\${VS_VERSION}&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
(start /w vs_buildtools.exe --quiet --wait --norestart --nocache `
  --installPath &quot;%VS%\%VS_VERSION%\BuildTools&quot; `
  --add Microsoft.VisualStudio.Workload.VCTools `
  --includeRecommended `
  || IF &quot;%ERRORLEVEL%&quot;==&quot;3010&quot; EXIT 0) &amp;amp;&amp;amp; `
del /q vs_buildtools.exe &amp;amp;&amp;amp; `
mklink /d &quot;%VS%\current&quot; &quot;%VS%\%VS_VERSION%&quot; &amp;amp;&amp;amp; `
if not exist &quot;%VS%\%VS_VERSION%\BuildTools\Common7\Tools\VsDevCmd.bat&quot; exit 1

&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;C:\\Program Files (x86)\\Microsoft Visual Studio\\current\\BuildTools\\VC\\Auxiliary\\Build\\vcvars64.bat&quot;, &quot;&amp;amp;&amp;amp;&quot;]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;SHELL&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;powershell&quot;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENV&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; chocolateyUseWindowsCompression false&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
Set-ExecutionPolicy Bypass -Scope Process -Force; `
[System.Net.ServicePointManager]::SecurityProtocol = `
  [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; `
iex ((New-Object System.Net.WebClient).DownloadString(`
  &apos;https://community.chocolatey.org/install.ps1&apos;))
&lt;span class=&quot;k&quot;&gt;SHELL&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;cmd&quot;, &quot;/S&quot;, &quot;/C&quot;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
call &quot;%VS%\current\BuildTools\VC\Auxiliary\Build\vcvars64.bat&quot; &amp;amp;&amp;amp; `
powershell -command `
  &quot;[Environment]::SetEnvironmentVariable(&apos;Path&apos;, $env:Path, [System.EnvironmentVariableTarget]::Machine)&quot;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
choco feature disable --name showDownloadProgress &amp;amp;&amp;amp; `
choco install -y --fail-on-error-output git -params &apos;&quot;/GitAndUnixToolsOnPath&quot;&apos; &amp;amp;&amp;amp; `
choco install -y --fail-on-error-output 7zip &amp;amp;&amp;amp; `
choco install -y --fail-on-error-output ccache
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;derivative-image&quot;&gt;Derivative image&lt;/h3&gt;

&lt;p&gt;When used as a base image in a derivative Dockerfile, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;bash.exe&lt;/code&gt; is an available shell. Because Windows Dockerfiles do not support extended Buildkit syntax features like heredocs, instructions must be strung together with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&amp;amp;&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# escape=`&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; windows-toolchain:latest&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;SHELL&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [&quot;bash.exe&quot;, &quot;-x&quot;, &quot;-e&quot;, &quot;-c&quot;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
curl -OSL --ssl-no-revoke https://zlib.net/zlib1213.zip &amp;amp;&amp;amp; `
7z.exe x zlib1213.zip &amp;amp;&amp;amp; `
rm zlib1213.zip &amp;amp;&amp;amp; `
cd zlib-1.2.13 &amp;amp;&amp;amp; `
mkdir build &amp;amp;&amp;amp; `
cd build &amp;amp;&amp;amp; `
cmake -G Ninja -D BUILD_SHARED_LIBS=YES .. &amp;amp;&amp;amp; `
cmake --build . --target INSTALL &amp;amp;&amp;amp; `
cd ../.. &amp;amp;&amp;amp; `
rm -rf zlib-1.2.13&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;build-environment&quot;&gt;Build environment&lt;/h3&gt;

&lt;p&gt;When used as a build environment, you can pass Bash code to be executed in with a Bash heredoc (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;&amp;lt;EOF&lt;/code&gt;).&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;wget https://zlib.net/zlib1213.zip
unzip zlib1213.zip
&lt;span class=&quot;nb&quot;&gt;rm &lt;/span&gt;zlib1213.zip
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;zlib-1.2.13
&lt;span class=&quot;nb&quot;&gt;mkdir &lt;/span&gt;build
docker run &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;--interactive&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;cygpath &lt;span class=&quot;nt&quot;&gt;-ad&lt;/span&gt; .&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;:&lt;span class=&quot;s1&quot;&gt;&apos;C:\work&apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;C:\work\build&apos;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  windows-toolchain:latest &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
  bash &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;&lt;span class=&quot;sh&quot;&gt;
cmake -G Ninja -D BUILD_SHARED_LIBS=YES ..
cmake --build . --target INSTALL
&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF
&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; ..
&lt;span class=&quot;nb&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-rf&lt;/span&gt; zlib-1.2.13
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Aside from the setup code in the windows-toolchain Dockerfile, no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cmd&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;powershell&lt;/code&gt; code is necessary to compile code using Visual Studio Build Tools. This allows automation to be written once in Bash, with minimal conditions for handling operating system differences.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Eliminating Code Duplication in Dockerfiles with Stage Name Aliasing</title>
   <author><name>Lisanna</name></author>
   <link href="https://lisanna.neocities.org/2023/02/03/eliminating-code-duplication-in-dockerfiles-with-stage-name-aliasing"/>
   <updated>2023-02-03T10:54:00-05:00</updated>
   <id>https://lisanna.neocities.org/2023/02/03/eliminating-code-duplication-in-dockerfiles-with-stage-name-aliasing</id>
   <content type="html">&lt;p&gt;The limited availability of traditional control flow constructs when writing Dockerfiles encourages code duplication. This increases the odds of errors made in subsequent updates wherein a developer may update one of the copies but accidentally forget to update the other(s). One alternative to these is &lt;a href=&quot;https://docs.docker.com/engine/reference/commandline/build/#-set-build-time-variables---build-arg&quot;&gt;build arguments&lt;/a&gt;, which can be leveraged to make decisions about which stages are the sources of other stages in &lt;a href=&quot;https://docs.docker.com/build/building/multi-stage/&quot;&gt;multi-stage builds&lt;/a&gt;. This allows you to design Dockerfiles with built-in conditionals, with each branch of the conditional being defined as its own stage, allowing shared code to be branch points instead of needing multiple copies of them in completely independent stages.&lt;/p&gt;

&lt;p&gt;One application of this is choosing between compiling a dependency versus downloading a pre-built binary.  Downloading a pre-built binary could be useful for testing that binary prior to release, and compiling it could be useful for debugging it on a local development machine.  Including a stage capable of compiling it as the dependency for its runtime stage allows developers to share a standardized declarative build environment and toolchain, making the results reproducible between developers.&lt;/p&gt;

&lt;p&gt;However, lack of support for build argument evaluation in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY --from&lt;/code&gt; directives discourages consolidating equivalent logic across these branched stages into a single downstream stage. Here’s an example with a Dockerfile for &lt;a href=&quot;https://stedolan.github.io/jq/&quot;&gt;jq&lt;/a&gt; which is alternately downloaded or built from source depending on a build argument, and then copies the binary into a runtime layer with only the dependencies required for execution.&lt;/p&gt;

&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# syntax=docker/dockerfile:1.5&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ARG&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; JQ_SOURCE=download&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;debian:bullseye&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;jq-build&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt-get update &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; build-essential libonig-dev
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; https://github.com/stedolan/jq/releases/download/jq-1.6/jq-1.6.tar.gz \&lt;/span&gt;
  jq-1.6.tar.gz
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;
tar xf jq-1.6.tar.gz
cd jq-1.6
./configure
make
make install
EOF

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;debian:bullseye-slim&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;jq-build-run&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt-get update &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; libonig5
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=jq-build /usr/local/bin/jq /usr/local/bin/jq&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;debian:bullseye&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;jq-download&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 \&lt;/span&gt;
  /usr/local/bin/jq
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x /usr/local/bin/jq

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;debian:bullseye-slim&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;jq-download-run&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt-get update &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; libonig5
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=jq-download /usr/local/bin/jq /usr/local/bin/jq&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;jq-${JQ_SOURCE}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;jq-run&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [ &quot;/usr/local/bin/jq&quot; ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Both the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jq-build&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jq-download&lt;/code&gt; stages produce an executable at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/bin/jq&lt;/code&gt; that can be copied into dependent stages, with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jq-*-run&lt;/code&gt; dependent stages being duplicates of each other, with the only difference being the stage from which the binary is copied.&lt;/p&gt;

&lt;div class=&quot;mermaid&quot;&gt;
graph LR;
    jq-build -. COPY --from .-&amp;gt; jq-build-run;
    jq-download -. COPY --from .-&amp;gt; jq-download-run;
    jq-build-run -- FROM --&amp;gt; jq-run;
    jq-download-run -- FROM --&amp;gt; jq-run;
&lt;/div&gt;

&lt;p&gt;As documented in &lt;a href=&quot;https://github.com/moby/moby/issues/34482&quot;&gt;moby/moby#34482&lt;/a&gt;, variables are not evaluated when they appear in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY --from=&lt;/code&gt; value. An attempt to consolidate the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jq-build-run&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jq-download-run&lt;/code&gt;, and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jq-run&lt;/code&gt; stages using a build argument directly would not work:&lt;/p&gt;

&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;debian:bullseye-slim&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;jq-run&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt-get update &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; libonig5
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=${JQ_SOURCE} /usr/local/bin/jq /usr/local/bin/jq&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [ &quot;/usr/local/bin/jq&quot; ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;However, because build arguments &lt;em&gt;are&lt;/em&gt; evaluated in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FROM&lt;/code&gt; statements, a stage can be conditionally aliased to a common name, which can be used as a dependency in both subsequent &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FROM&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY --from=&lt;/code&gt; statements. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jq-source&lt;/code&gt; is used as an alias stage in this modified version.&lt;/p&gt;

&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# syntax=docker/dockerfile:1.5&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;ARG&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; JQ_SOURCE=download&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;debian:bullseye&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;jq-build&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt-get update &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; build-essential libonig-dev
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; https://github.com/stedolan/jq/releases/download/jq-1.6/jq-1.6.tar.gz \&lt;/span&gt;
  jq-1.6.tar.gz
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;EOF&lt;/span&gt;
tar xf jq-1.6.tar.gz
cd jq-1.6
./configure
make
make install
EOF

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;debian:bullseye&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;jq-download&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ADD&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 \&lt;/span&gt;
  /usr/local/bin/jq
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;chmod&lt;/span&gt; +x /usr/local/bin/jq

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;jq-${JQ_SOURCE}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;jq-source&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;debian:bullseye&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;k&quot;&gt;AS&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s&quot;&gt;final&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt-get update &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; libonig5
&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=jq-source /usr/local/bin/jq /usr/local/bin/jq&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;ENTRYPOINT&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; [ &quot;/usr/local/bin/jq&quot; ]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This produces a dependency graph with fewer stages and no code duplication.&lt;/p&gt;

&lt;div class=&quot;mermaid&quot;&gt;
graph LR;
    jq-build -- FROM --&amp;gt; jq-source;
    jq-download -- FROM --&amp;gt; jq-source;
    jq-source -. COPY --from .-&amp;gt; jq-run
&lt;/div&gt;

&lt;p&gt;Using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;JQ_SOURCE&lt;/code&gt; argument as a conditional switch, this image can be built in two different ways:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker buildx build &lt;span class=&quot;nt&quot;&gt;--build-arg&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;JQ_SOURCE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;download - &amp;lt; Dockerfile
docker buildx build &lt;span class=&quot;nt&quot;&gt;--build-arg&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;JQ_SOURCE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;build - &amp;lt; Dockerfile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In the first &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;docker buildx build&lt;/code&gt; command, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jq-source&lt;/code&gt; evaluates to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jq-download&lt;/code&gt;, causing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;COPY&lt;/code&gt; statement in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;final&lt;/code&gt; stage to evaluate to:&lt;/p&gt;

&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=jq-download /usr/local/bin/jq /usr/local/bin/jq&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And likewise for the second build command:&lt;/p&gt;

&lt;div class=&quot;language-Dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;COPY&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; --from=jq-build /usr/local/bin/jq /usr/local/bin/jq&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This demonstrates how aliasing stage names computed from build arguments can be used to implement sophisticated conditional logic about how different stages can depend on each other, preventing the need to write multiple Dockerfiles or to duplicate code within a Dockerfile.&lt;/p&gt;
</content>
 </entry>
 

</feed>
