<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Scott Cotton</title>
    <description>The latest articles on Forem by Scott Cotton (@scott_cotton_dc9ce3e7e632).</description>
    <link>https://forem.com/scott_cotton_dc9ce3e7e632</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3699965%2F5404d330-9b91-49ab-be6a-6d1b6d033917.jpg</url>
      <title>Forem: Scott Cotton</title>
      <link>https://forem.com/scott_cotton_dc9ce3e7e632</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/scott_cotton_dc9ce3e7e632"/>
    <language>en</language>
    <item>
      <title>Wrote a SOTU satire on substack.
Enjoy
https://scottcotton.substack.com/p/state-of-the-union-address</title>
      <dc:creator>Scott Cotton</dc:creator>
      <pubDate>Wed, 25 Feb 2026 13:57:38 +0000</pubDate>
      <link>https://forem.com/scott_cotton_dc9ce3e7e632/wrote-a-sotu-satire-on-substack-enjoy-3d96</link>
      <guid>https://forem.com/scott_cotton_dc9ce3e7e632/wrote-a-sotu-satire-on-substack-enjoy-3d96</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://scottcotton.substack.com/p/state-of-the-union-address" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21WaBA%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fscottcotton.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D1724395976%2526version%253D9" height="480" class="m-0" width="920"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://scottcotton.substack.com/p/state-of-the-union-address" rel="noopener noreferrer" class="c-link"&gt;
            State of the Union Address - Scott Cotton
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            As Delivered by the President of the Provisional Government of Free America February 25, 2026 — Undisclosed Location (Formerly a Dave &amp;amp; Buster's)
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Ficons%2Fsubstack%2Ffavicon.ico" width="32" height="32"&gt;
          scottcotton.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Docs are finally live for Tony format: https://signadot.github.io/tony-format/</title>
      <dc:creator>Scott Cotton</dc:creator>
      <pubDate>Wed, 18 Feb 2026 11:56:44 +0000</pubDate>
      <link>https://forem.com/scott_cotton_dc9ce3e7e632/docs-are-finally-live-for-tony-format-httpssignadotgithubiotony-format-1g02</link>
      <guid>https://forem.com/scott_cotton_dc9ce3e7e632/docs-are-finally-live-for-tony-format-httpssignadotgithubiotony-format-1g02</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/scott_cotton_dc9ce3e7e632" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3699965%2F5404d330-9b91-49ab-be6a-6d1b6d033917.jpg" alt="scott_cotton_dc9ce3e7e632"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/scott_cotton_dc9ce3e7e632/structured-matching-patching-and-diffing-done-right-22ci" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Structured Matching, Patching, and Diffing Done Right&lt;/h2&gt;
      &lt;h3&gt;Scott Cotton ・ Jan 29&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#automation&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#kubernetes&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tooling&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://signadot.github.io/tony-format/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;signadot.github.io&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>automation</category>
      <category>devops</category>
      <category>kubernetes</category>
      <category>tooling</category>
    </item>
    <item>
      <title>ArgoCD makes it surprisingly hard to supply manifests and update images outside Helm/Kustomize. Here's how o build and ORAS cut through the friction.</title>
      <dc:creator>Scott Cotton</dc:creator>
      <pubDate>Mon, 09 Feb 2026 13:41:06 +0000</pubDate>
      <link>https://forem.com/scott_cotton_dc9ce3e7e632/argocd-makes-it-surprisingly-hard-to-supply-manifests-and-update-images-outside-helmkustomize-28df</link>
      <guid>https://forem.com/scott_cotton_dc9ce3e7e632/argocd-makes-it-surprisingly-hard-to-supply-manifests-and-update-images-outside-helmkustomize-28df</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/scott_cotton_dc9ce3e7e632" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3699965%2F5404d330-9b91-49ab-be6a-6d1b6d033917.jpg" alt="scott_cotton_dc9ce3e7e632"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/scott_cotton_dc9ce3e7e632/how-to-navigate-argocds-manifest-friction-with-oras-and-o-build-3h47" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;How to Navigate ArgoCD's Manifest Friction with ORAS and `o build`&lt;/h2&gt;
      &lt;h3&gt;Scott Cotton ・ Feb 9&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#kubernetes&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#argocd&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#gitops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>kubernetes</category>
      <category>argocd</category>
      <category>gitops</category>
      <category>devops</category>
    </item>
    <item>
      <title>How to Navigate ArgoCD's Manifest Friction with ORAS and `o build`</title>
      <dc:creator>Scott Cotton</dc:creator>
      <pubDate>Mon, 09 Feb 2026 13:39:05 +0000</pubDate>
      <link>https://forem.com/scott_cotton_dc9ce3e7e632/how-to-navigate-argocds-manifest-friction-with-oras-and-o-build-3h47</link>
      <guid>https://forem.com/scott_cotton_dc9ce3e7e632/how-to-navigate-argocds-manifest-friction-with-oras-and-o-build-3h47</guid>
      <description>&lt;h2&gt;
  
  
  ArgoCD's Blind Spot
&lt;/h2&gt;

&lt;p&gt;ArgoCD has one job: take Kubernetes manifests and apply them to a cluster. It does this well. It watches a git repo, detects drift, syncs state, shows you diffs. What it doesn't do well — surprisingly — is accept manifests.&lt;/p&gt;

&lt;p&gt;Out of the box, ArgoCD understands three things: plain YAML directories, Helm charts, and Kustomize overlays. Anything else requires a Config Management Plugin (CMP), which means building a container image, writing a plugin spec, and mounting it as a sidecar on the repo-server.&lt;/p&gt;

&lt;p&gt;That's already more friction than you'd expect for a tool whose entire purpose is "take YAML, apply it." But the real problem goes deeper.&lt;/p&gt;

&lt;h3&gt;
  
  
  argocd-image-updater Doesn't Work with Plugins
&lt;/h3&gt;

&lt;p&gt;The most common operation in a deployment pipeline is updating an image tag. ArgoCD's answer to this is &lt;a href="https://argocd-image-updater.readthedocs.io/" rel="noopener noreferrer"&gt;argocd-image-updater&lt;/a&gt;, a separate controller that watches registries and writes back image tags. It works with Helm (by updating &lt;code&gt;values.yaml&lt;/code&gt; parameters) and Kustomize (by updating &lt;code&gt;kustomization.yaml&lt;/code&gt;). It does &lt;strong&gt;not&lt;/strong&gt; work with Config Management Plugins.&lt;/p&gt;

&lt;p&gt;This means if you use a CMP — whether it's jsonnet, cue, dhall, or anything else — you're on your own for image updates. ArgoCD's manifest pipeline and its image-update pipeline are two systems that don't talk to each other unless you happen to use one of the two blessed tools.&lt;/p&gt;

&lt;p&gt;So a tool built to apply manifests makes it hard to supply manifests, and its image updater only works with specific manifest generators. This is the landscape.&lt;/p&gt;

&lt;h2&gt;
  
  
  What &lt;code&gt;o build&lt;/code&gt; Does
&lt;/h2&gt;

&lt;p&gt;Tony format is a data format where patches, queries, and schemas are expressed as documents themselves — combining JSON's structure with YAML's readability. (For background on Tony's core operations, see &lt;a href="https://dev.to/scott_cotton_dc9ce3e7e632/structured-matching-patching-and-diffing-done-right-22ci"&gt;Structured Matching, Patching, and Diffing Done Right&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;Its CLI tool &lt;code&gt;o&lt;/code&gt; includes a build system driven by a &lt;code&gt;build.tony&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build:
  env:
    namespace: default
    replicas: 3

  sources:
  - dir: ./manifests

  patches:
  - match: { kind: Deployment }
    patch:
      metadata:
        namespace: .[namespace]
      spec:
        replicas: .[replicas]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sources fetch documents from directories, URLs, or command output. Patches are applied conditionally using match patterns. The &lt;code&gt;.[varname]&lt;/code&gt; expressions perform typed substitution — not string interpolation, but structural replacement within the IR tree. (The &lt;a href="https://dev.to/scott_cotton_dc9ce3e7e632/evaluation-in-tony-format-4cnl"&gt;evaluation system&lt;/a&gt; covers how these expressions compose with &lt;code&gt;!eval&lt;/code&gt;, &lt;code&gt;!exec&lt;/code&gt;, &lt;code&gt;!file&lt;/code&gt;, and other tags.) Profiles in a &lt;code&gt;profiles/&lt;/code&gt; subdirectory override the environment per deployment target.&lt;/p&gt;

&lt;p&gt;The key idea: patches are documents with the same structure as the data they operate on. No templating language. &lt;code&gt;!key(name)&lt;/code&gt; lets you merge arrays by a field — something Kustomize can only do for hardcoded Kubernetes merge keys, but Tony lets you do for any array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;o build &lt;span class="nt"&gt;-y&lt;/span&gt;                      &lt;span class="c"&gt;# output as YAML&lt;/span&gt;
o build &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; staging           &lt;span class="c"&gt;# with a profile&lt;/span&gt;
o build &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prod    &lt;span class="c"&gt;# override a single variable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you also need to produce Helm charts from the same source manifests, &lt;code&gt;o build&lt;/code&gt; handles that too — see &lt;a href="https://dev.to/scott_cotton_dc9ce3e7e632/one-source-two-outputs-generating-k8s-yaml-and-helm-charts-with-tony-format-30h6"&gt;One Source, Two Outputs&lt;/a&gt; for how Tony generates both K8s YAML and Helm charts from a single build directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solving Image Updates Without argocd-image-updater
&lt;/h2&gt;

&lt;p&gt;Since argocd-image-updater doesn't work with plugins, we need another way to get per-commit image tags into the build. The approach: store image tags as OCI artifacts using &lt;a href="https://oras.land" rel="noopener noreferrer"&gt;ORAS&lt;/a&gt; and pull them at sync time.&lt;/p&gt;

&lt;h3&gt;
  
  
  CI Pushes Image Tags
&lt;/h3&gt;

&lt;p&gt;After building images, CI publishes a small JSON artifact to the registry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; image-tags.json &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
{"env": {"images": {"tags": {"app": "sha-abc123", "worker": "sha-def456"}}}}
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;oras push ghcr.io/org/deploy-manifest:&lt;span class="nv"&gt;$COMMIT_SHA&lt;/span&gt; image-tags.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;env:&lt;/code&gt; wrapper makes this a valid &lt;code&gt;o build&lt;/code&gt; profile — it merges into the build environment naturally.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Plugin Pulls at Sync Time
&lt;/h3&gt;

&lt;p&gt;The CMP's generate command pulls the artifact for the current commit and pipes it to &lt;code&gt;o build&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argoproj.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ConfigManagementPlugin&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tony-build&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1.0&lt;/span&gt;
  &lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sh&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;-c&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;if oras pull ghcr.io/org/deploy-manifest:$ARGOCD_APP_REVISION -o /tmp 2&amp;gt;/dev/null; then&lt;/span&gt;
        &lt;span class="s"&gt;cat /tmp/image-tags.json | o build -y $ARGOCD_ENV_ARGS .&lt;/span&gt;
      &lt;span class="s"&gt;else&lt;/span&gt;
        &lt;span class="s"&gt;echo "manifest not available for $ARGOCD_APP_REVISION, scheduling retry" &amp;gt;&amp;amp;2&lt;/span&gt;
        &lt;span class="s"&gt;ARGOCD_TOKEN=$(cat /argocd-api-token/token)&lt;/span&gt;
        &lt;span class="s"&gt;(sleep 60 &amp;amp;&amp;amp; wget -qO- \&lt;/span&gt;
          &lt;span class="s"&gt;--header="Authorization: Bearer $ARGOCD_TOKEN" \&lt;/span&gt;
          &lt;span class="s"&gt;"http://argocd-server.argocd.svc/api/v1/applications/$ARGOCD_APP_NAME?refresh=hard" \&lt;/span&gt;
          &lt;span class="s"&gt;&amp;gt; /dev/null 2&amp;gt;&amp;amp;1) &amp;amp;&lt;/span&gt;
        &lt;span class="s"&gt;exit 1&lt;/span&gt;
      &lt;span class="s"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Timing Problem
&lt;/h3&gt;

&lt;p&gt;When a commit is pushed, two things happen in parallel: CI starts building images, and ArgoCD notices the new commit and tries to sync. ArgoCD is usually faster. The &lt;code&gt;oras pull&lt;/code&gt; fails because the artifact doesn't exist yet.&lt;/p&gt;

&lt;p&gt;ArgoCD will sometimes retry manifest generation errors on its own, but this isn't reliable — the repo-server can cache the error and stop trying. So the plugin handles retries explicitly: on failure, it backgrounds a process that waits 60 seconds and triggers a hard refresh via the ArgoCD API. The hard refresh clears the cached error and ArgoCD re-invokes the plugin. This repeats until the artifact appears.&lt;/p&gt;

&lt;p&gt;It's a workaround for another ArgoCD gap — no built-in way for a plugin to say "not ready yet, try again soon." But it works.&lt;/p&gt;

&lt;h3&gt;
  
  
  What This Gets You
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No git commits for image updates&lt;/strong&gt; — tags live in the registry alongside the images themselves&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No argocd-image-updater&lt;/strong&gt; — the plugin handles everything, for any manifest generator&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic deploys&lt;/strong&gt; — all image tags for a commit ship as one artifact&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decoupled CI&lt;/strong&gt; — CI just pushes OCI artifacts, needs no ArgoCD access&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Application
&lt;/h2&gt;

&lt;p&gt;Here's what an ArgoCD Application looks like using &lt;code&gt;o build&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argoproj.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Application&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-operator&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;repoURL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/org/repo.git&lt;/span&gt;
    &lt;span class="na"&gt;targetRevision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HEAD&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy/operator&lt;/span&gt;
    &lt;span class="na"&gt;plugin&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tony-build-v1.0&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ARGS&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-p&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;staging&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-p&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-"&lt;/span&gt;
  &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://kubernetes.default.svc&lt;/span&gt;
  &lt;span class="na"&gt;syncPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;syncOptions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ApplyOutOfSyncOnly=true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;plugin.name: tony-build-v1.0&lt;/code&gt;&lt;/strong&gt; — tells ArgoCD to use our CMP instead of Helm or Kustomize&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;env.ARGS: "-p staging -p -"&lt;/code&gt;&lt;/strong&gt; — passes flags to &lt;code&gt;o build&lt;/code&gt;: apply the &lt;code&gt;staging&lt;/code&gt; profile, then overlay image tags from stdin via &lt;code&gt;-p -&lt;/code&gt;. We place these args in the Application to be able to customize them without updating the plugin. We may want to add &lt;code&gt;-e serviceMesh=istio&lt;/code&gt; for example.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;deploy/operator&lt;/code&gt; directory in that repo contains a &lt;code&gt;build.tony&lt;/code&gt; file. ArgoCD clones the repo, hands the directory to the plugin, and the plugin runs &lt;code&gt;o build&lt;/code&gt; to produce YAML on stdout. Whatever comes out is what ArgoCD applies. Different Applications use different profiles from the same build directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing the Plugin
&lt;/h2&gt;

&lt;p&gt;A CMP runs as a sidecar on the &lt;code&gt;argocd-repo-server&lt;/code&gt; deployment. You need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A ConfigMap with the plugin spec&lt;/li&gt;
&lt;li&gt;A sidecar container with &lt;code&gt;o&lt;/code&gt; and &lt;code&gt;oras&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Registry credentials for pulling artifacts&lt;/li&gt;
&lt;li&gt;An ArgoCD API token for the retry mechanism&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The sidecar looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tony-build&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/signadot/tony-plugin:latest&lt;/span&gt;
  &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;/var/run/argocd/argocd-cmp-server&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;securityContext&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runAsNonRoot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;runAsUser&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;999&lt;/span&gt;
  &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;var-files&lt;/span&gt;
    &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/run/argocd&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;plugins&lt;/span&gt;
    &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/home/argocd/cmp-server/plugins&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tony-plugin-config&lt;/span&gt;
    &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/home/argocd/cmp-server/config/plugin.yaml&lt;/span&gt;
    &lt;span class="na"&gt;subPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;plugin.yaml&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tmp&lt;/span&gt;
    &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/tmp&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr-creds&lt;/span&gt;
    &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/ghcr-creds&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;argocd-api-token&lt;/span&gt;
    &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/argocd-api-token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Patching ArgoCD with &lt;code&gt;o build&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Rather than hand-editing the repo-server Deployment, you can use &lt;code&gt;o build&lt;/code&gt; itself to do the patching. The tony-format repo includes an &lt;a href="https://github.com/signadot/tony-format/tree/main/go-tony/examples/build/patch-argocd" rel="noopener noreferrer"&gt;example&lt;/a&gt; that fetches the live Deployment from the cluster and patches it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build:
  sources:
  - exec: "kubectl get deployment argocd-repo-server -n .[namespace] -o yaml"
    format: yaml
  - dir: source

  env:
    namespace: argocd
    image: ghcr.io/signadot/tony:latest
    pluginName: tony-build

  patches:
  - file: patches/cleanup.tony
  - file: patches/namespace.tony
  - file: patches/repo-server.tony
  - file: patches/application.tony
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;repo-server.tony&lt;/code&gt; patch uses &lt;code&gt;!key(name)&lt;/code&gt; to &lt;a href="https://dev.to/scott_cotton_dc9ce3e7e632/structured-matching-patching-and-diffing-done-right-22ci"&gt;merge arrays by field&lt;/a&gt; — adding the sidecar container without knowing or caring what else is in the containers array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- match:
    kind: Deployment
    metadata:
      name: argocd-repo-server
  patch:
    spec:
      template:
        spec:
          containers: !key(name)
          - name: tony-build
            image: .[image]
            command: [/var/run/argocd/argocd-cmp-server]
            securityContext:
              runAsNonRoot: true
              runAsUser: 999
            volumeMounts:
            - name: var-files
              mountPath: /var/run/argocd
            - name: plugins
              mountPath: /home/argocd/cmp-server/plugins
            - name: tony-plugin-config
              mountPath: /home/argocd/cmp-server/config/plugin.yaml
              subPath: plugin.yaml
            - name: tmp
              mountPath: /tmp
          volumes: !key(name)
          - name: tony-plugin-config
            configMap:
              name: tony-build-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;o build examples/build/patch-argocd &lt;span class="nt"&gt;-y&lt;/span&gt; | kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;ArgoCD is good at what it does — reconciling cluster state against a desired state. But it makes assumptions about how that desired state is produced, and those assumptions break down as soon as you step outside Helm or Kustomize. The plugin system exists but is a second-class citizen. The image updater ignores it entirely.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;o build&lt;/code&gt; as a CMP gives you a manifest generator that works in &lt;a href="https://dev.to/scott_cotton_dc9ce3e7e632/structured-matching-patching-and-diffing-done-right-22ci"&gt;structured documents&lt;/a&gt; rather than templates, with conditional patches, &lt;a href="https://dev.to/scott_cotton_dc9ce3e7e632/evaluation-in-tony-format-4cnl"&gt;composable evaluation&lt;/a&gt;, environment profiles, and multi-source builds. The ORAS pattern gives you per-commit image updates without git-commit noise and without depending on argocd-image-updater.&lt;/p&gt;

&lt;p&gt;It shouldn't require this much machinery to give a manifest-applier some manifests. But given ArgoCD's constraints, this is a functionally complete way through without common drawbacks like storing non-source generated manifests in git.&lt;/p&gt;




&lt;p&gt;Install &lt;code&gt;o&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go &lt;span class="nb"&gt;install &lt;/span&gt;github.com/signadot/tony-format/go-tony/cmd/o@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full example: &lt;a href="https://github.com/signadot/tony-format/tree/main/go-tony/examples/build/patch-argocd" rel="noopener noreferrer"&gt;&lt;code&gt;examples/build/patch-argocd&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;a href="https://github.com/signadot/tony-format" rel="noopener noreferrer"&gt;Tony format&lt;/a&gt; is open source.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>argocd</category>
      <category>gitops</category>
      <category>devops</category>
    </item>
    <item>
      <title>The Invisible Asymmetry discusses the impact of agentic/llm explosion. Cross-cutting and well grounded -- Worth a look!

https://open.substack.com/pub/scottcotton/p/the-invisble-asymmetry?r=5cjm1c&amp;utm_campaign=post&amp;utm_medium=web</title>
      <dc:creator>Scott Cotton</dc:creator>
      <pubDate>Sat, 07 Feb 2026 12:28:19 +0000</pubDate>
      <link>https://forem.com/scott_cotton_dc9ce3e7e632/the-invisible-asymmetry-discusses-the-impact-of-agenticllm-explosion-cross-cutting-and-well-pc</link>
      <guid>https://forem.com/scott_cotton_dc9ce3e7e632/the-invisible-asymmetry-discusses-the-impact-of-agenticllm-explosion-cross-cutting-and-well-pc</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://scottcotton.substack.com/p/the-invisble-asymmetry?r=5cjm1c&amp;amp;amp%3Butm_campaign=post&amp;amp;amp%3Butm_medium=web&amp;amp;triedRedirect=true" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21WaBA%21%2Cf_auto%2Cq_auto%3Abest%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fscottcotton.substack.com%252Ftwitter%252Fsubscribe-card.jpg%253Fv%253D1724395976%2526version%253D9" height="480" class="m-0" width="920"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://scottcotton.substack.com/p/the-invisble-asymmetry?r=5cjm1c&amp;amp;amp%3Butm_campaign=post&amp;amp;amp%3Butm_medium=web&amp;amp;triedRedirect=true" rel="noopener noreferrer" class="c-link"&gt;
            The Invisble Asymmetry - Scott Cotton
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            When you talk to me, I listen.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Ficons%2Fsubstack%2Ffavicon.ico" width="32" height="32"&gt;
          scottcotton.substack.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>The only article I've seen whose cover image legitimately shows trees running on a track.</title>
      <dc:creator>Scott Cotton</dc:creator>
      <pubDate>Wed, 04 Feb 2026 09:00:16 +0000</pubDate>
      <link>https://forem.com/scott_cotton_dc9ce3e7e632/the-only-article-ive-seen-whose-cover-image-legitimately-shows-trees-running-on-a-track-1a55</link>
      <guid>https://forem.com/scott_cotton_dc9ce3e7e632/the-only-article-ive-seen-whose-cover-image-legitimately-shows-trees-running-on-a-track-1a55</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/scott_cotton_dc9ce3e7e632" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3699965%2F5404d330-9b91-49ab-be6a-6d1b6d033917.jpg" alt="scott_cotton_dc9ce3e7e632"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/scott_cotton_dc9ce3e7e632/evaluation-in-tony-format-4cnl" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Evaluation in Tony Format&lt;/h2&gt;
      &lt;h3&gt;Scott Cotton ・ Feb 4&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#architecture&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#automation&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tooling&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>architecture</category>
      <category>automation</category>
      <category>opensource</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Evaluation in Tony Format</title>
      <dc:creator>Scott Cotton</dc:creator>
      <pubDate>Wed, 04 Feb 2026 08:49:32 +0000</pubDate>
      <link>https://forem.com/scott_cotton_dc9ce3e7e632/evaluation-in-tony-format-4cnl</link>
      <guid>https://forem.com/scott_cotton_dc9ce3e7e632/evaluation-in-tony-format-4cnl</guid>
      <description>&lt;p&gt;A &lt;a href="https://dev.to/scott_cotton_dc9ce3e7e632/structured-matching-patching-and-diffing-done-right-22ci"&gt;companion article&lt;/a&gt; describes how &lt;a href="https://github.com/signadot/tony-format" rel="noopener noreferrer"&gt;Tony format&lt;/a&gt;'s matching, patching, and diffing operations all share the same IR and tag system. This article covers a fourth operation---evaluation---which uses the same tag mechanism to expand expressions, load files, execute commands, and thread an environment through a document tree.&lt;/p&gt;

&lt;p&gt;The evaluation mechanism isn't fundamentally different from expansion systems in other tools. YAML's tag syntax (&lt;code&gt;!&lt;/code&gt; prefix) is already used this way across the industry: CloudFormation's &lt;code&gt;!Ref&lt;/code&gt;, &lt;code&gt;!Sub&lt;/code&gt;, and &lt;code&gt;!GetAtt&lt;/code&gt; wire resources together at deploy time; Home Assistant's &lt;code&gt;!include&lt;/code&gt; and &lt;code&gt;!secret&lt;/code&gt; split configuration across files; SpeechBrain's HyperPyYAML uses &lt;code&gt;!ref&lt;/code&gt; for cross-references and &lt;code&gt;!new&lt;/code&gt; to instantiate Python objects at parse time. Outside YAML-specific tools, the same pattern appears in Jinja2's template rendering, Helm's Go templates, and jsonnet's late binding---walk a tree or string, find markers, substitute values.&lt;/p&gt;

&lt;p&gt;What makes Tony's version worth writing about is not novelty in the expansion mechanism itself, but how it fits into the same IR and tag system that drives match, patch, and diff. CloudFormation tags only wire resources. HyperPyYAML tags only construct Python objects. Tony's eval tags are symbols in the same registry as &lt;code&gt;!or&lt;/code&gt;, &lt;code&gt;!key(name)&lt;/code&gt;, and &lt;code&gt;!dive&lt;/code&gt;, operating on the same &lt;code&gt;ir.Node&lt;/code&gt; tree, composing with everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Evaluation is a post-order tree walk. The engine traverses the &lt;code&gt;ir.Node&lt;/code&gt; tree depth-first, processing children before parents. At each node, if the node carries an eval tag (&lt;code&gt;!eval&lt;/code&gt;, &lt;code&gt;!file&lt;/code&gt;, &lt;code&gt;!exec&lt;/code&gt;, &lt;code&gt;!script&lt;/code&gt;, etc.), the engine looks up the corresponding symbol in the eval registry, instantiates an operation, and calls its &lt;code&gt;Eval&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Op&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="n"&gt;Env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;EvalFunc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Env&lt;/code&gt; is a &lt;code&gt;map[string]any&lt;/code&gt; threaded through every call. The &lt;code&gt;EvalFunc&lt;/code&gt; callback enables recursive delegation back to the engine, same pattern as &lt;code&gt;MatchFunc&lt;/code&gt; and &lt;code&gt;PatchFunc&lt;/code&gt; in the merge operations. New eval operations are added by registering a symbol---no changes to the engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expression syntax
&lt;/h2&gt;

&lt;p&gt;Tony supports two expression forms in string values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;.[expression]&lt;/code&gt;&lt;/strong&gt; --- replaces the entire node. If the string is exactly &lt;code&gt;.[x]&lt;/code&gt;, the node is replaced with whatever &lt;code&gt;x&lt;/code&gt; evaluates to---a number, an object, an array, null. The result is typed, not stringified.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;$[expression]&lt;/code&gt;&lt;/strong&gt; --- string interpolation. The expression result is converted to a string and spliced into the surrounding text. Multiple &lt;code&gt;$[...]&lt;/code&gt; expressions can appear in a single string.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Expressions are evaluated with &lt;a href="https://expr-lang.org/" rel="noopener noreferrer"&gt;expr-lang&lt;/a&gt;, a Go expression evaluator. Variables come from the threaded environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="kt"&gt;!eval&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.[name]&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.[namespace]&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v$[version]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;env = {name: "frontend", namespace: "prod", version: "1.2.3"}&lt;/code&gt;, this produces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1.2.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The distinction between &lt;code&gt;.[...]&lt;/code&gt; and &lt;code&gt;$[...]&lt;/code&gt; matters. &lt;code&gt;.[replicas]&lt;/code&gt; where &lt;code&gt;replicas&lt;/code&gt; is the integer 3 produces the number 3, not the string &lt;code&gt;"3"&lt;/code&gt;. &lt;code&gt;$[replicas]&lt;/code&gt; produces the string &lt;code&gt;"3"&lt;/code&gt;. CloudFormation has a similar two-form split---&lt;code&gt;!Ref&lt;/code&gt; for references, &lt;code&gt;!Sub&lt;/code&gt; for string interpolation---but both produce strings in the end. Tony's &lt;code&gt;.[...]&lt;/code&gt; produces typed IR nodes: objects, arrays, numbers, not just strings. This is what keeps evaluation type-safe---markers expand to structured data, not text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Script functions
&lt;/h2&gt;

&lt;p&gt;When evaluating expressions, several built-in functions are available that operate on the document tree itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;whereami()&lt;/code&gt;&lt;/strong&gt; --- returns the path of the current node (e.g. &lt;code&gt;$.metadata.name&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;getpath(path)&lt;/code&gt;&lt;/strong&gt; --- navigates to any node in the document by path&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;listpath(path)&lt;/code&gt;&lt;/strong&gt; --- returns multiple nodes matching a path pattern&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;getenv(name)&lt;/code&gt;&lt;/strong&gt; --- reads an OS environment variable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These functions give expressions access to the document's own structure. A value can reference another value elsewhere in the same tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="kt"&gt;!eval&lt;/span&gt;
&lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;appName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-app&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres.example.com&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5432&lt;/span&gt;
&lt;span class="na"&gt;deployment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.[getpath("$.config.appName").String]'&lt;/span&gt;
  &lt;span class="na"&gt;dsn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.[getpath("$.config.database.host").String&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;+&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;":"&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;+&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;string(getpath("$.config.database.port").Int64)]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is self-referential expansion: the document is both the template and the data source. HyperPyYAML's &lt;code&gt;!ref&lt;/code&gt; tag does something similar---&lt;code&gt;!ref &amp;lt;key&amp;gt;&lt;/code&gt; resolves a value from elsewhere in the same YAML file, with support for dotted paths and simple arithmetic. Tony's &lt;code&gt;getpath&lt;/code&gt; navigates arbitrary paths through the typed IR tree, and because it returns an &lt;code&gt;ir.Node&lt;/code&gt;, the result composes with further expressions.&lt;/p&gt;

&lt;h2&gt;
  
  
  The eval tags
&lt;/h2&gt;

&lt;p&gt;The built-in eval operations, all registered as symbols in the same registry:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tag&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!eval&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Expand &lt;code&gt;.[...]&lt;/code&gt; and &lt;code&gt;$[...]&lt;/code&gt; expressions in the subtree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Load content from a local file or HTTP URL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!exec&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run a shell command, capture stdout&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!script&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Evaluate an expr-lang program (with output mode: value, string, or any)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!osenv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read OS environment variables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!tostring&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Convert a node to its string representation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!toint&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Convert to integer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!tovalue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Parse a YAML string into a typed node&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;!b64enc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Base64 encode&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;!file&lt;/code&gt; and &lt;code&gt;!exec&lt;/code&gt; are the I/O operations. &lt;code&gt;!file&lt;/code&gt; loads a file path or URL that can itself contain &lt;code&gt;.[...]&lt;/code&gt; references (expanded before loading). &lt;code&gt;!exec&lt;/code&gt; runs a shell command and returns stdout as a string node. Both are simple---there's no caching, no retry logic, no abstraction layer. They do one thing. This is a deliberate trade-off against tools like Carvel's ytt, which is fully sandboxed (no filesystem, no network, no shell). Tony chooses to allow side effects and marks them: &lt;code&gt;!exec&lt;/code&gt; and &lt;code&gt;!pipe&lt;/code&gt; are flagged as unsafe operations that callers can opt out of.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;!script&lt;/code&gt; provides full expr-lang evaluation with configurable output. With &lt;code&gt;!script(value)&lt;/code&gt;, the result string is parsed as YAML back into IR. With &lt;code&gt;!script(string)&lt;/code&gt;, it stays a string. With &lt;code&gt;!script(any)&lt;/code&gt;, the raw expr-lang result is converted directly to an IR node.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tag composition
&lt;/h2&gt;

&lt;p&gt;Tags compose via dot-separation. &lt;code&gt;!tovalue.file&lt;/code&gt; chains two operations on a single node: &lt;code&gt;!file&lt;/code&gt; loads the file, &lt;code&gt;!tovalue&lt;/code&gt; parses the result as YAML. &lt;code&gt;!not.or&lt;/code&gt; negates a disjunction. &lt;code&gt;!subtree.type&lt;/code&gt; searches depth-first for nodes of a given type. The engine peels off the first tag, executes it, and passes the rest of the chain to the child---a pipeline that reads right to left. Tags also carry arguments inline: &lt;code&gt;!key(name)&lt;/code&gt;, &lt;code&gt;!script(value)&lt;/code&gt;, &lt;code&gt;!delete(!mytag)&lt;/code&gt;. Other tools that use YAML tags---CloudFormation, HyperPyYAML, authentik blueprints---achieve composition by nesting child nodes: variable maps, extra arrays, separate mapping entries. The YAML spec assigns one tag per node, so stacking tags means adding structure. Tony's dot-separated tags put multiple operations on a &lt;em&gt;single&lt;/em&gt; node's tag string. The result: document structure preservation. The operations ride on the tag; they don't reshape the data to accommodate themselves.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Load a YAML file and merge it into the current object&lt;/span&gt;
&lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!tovalue.file&lt;/span&gt; &lt;span class="s"&gt;config/cluster.yaml&lt;/span&gt;

&lt;span class="c1"&gt;# Run a command, parse its stdout as a typed value&lt;/span&gt;
&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!tovalue.exec&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;echo&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;42"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Composition with &lt;code&gt;o build&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The build system is where evaluation becomes practical. A &lt;code&gt;build.tony&lt;/code&gt; file declares sources, patches, an environment, and output configuration. The environment is where &lt;code&gt;!eval&lt;/code&gt;, &lt;code&gt;!exec&lt;/code&gt;, and &lt;code&gt;!file&lt;/code&gt; do their work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build:
  env:
    namespace: default
    version: !exec ./version.sh
    debug: true

  sources:
  - dir: ./manifests

  patches:
  - match: { kind: Deployment }
    patch: { metadata: { namespace: .[namespace] } }
  - file: extra-patches.tony
    if: .[debug]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The environment is itself evaluated---&lt;code&gt;!exec ./version.sh&lt;/code&gt; runs at build time and its stdout becomes the value of &lt;code&gt;version&lt;/code&gt;. Then every &lt;code&gt;.[namespace]&lt;/code&gt; and &lt;code&gt;.[debug]&lt;/code&gt; reference in patches expands against that resolved environment. Patches are conditional: &lt;code&gt;if: .[debug]&lt;/code&gt; means the patch only applies when &lt;code&gt;debug&lt;/code&gt; is truthy.&lt;/p&gt;

&lt;p&gt;Environment values are set with increasing precedence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;env:&lt;/code&gt; field in the build file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-e key=value&lt;/code&gt; flags&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-- key1=val1 key2=val2&lt;/code&gt; arguments&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$TONY_DIRBUILD_ENV&lt;/code&gt; OS environment variable&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Profiles---override files in a &lt;code&gt;profiles/&lt;/code&gt; subdirectory---patch the base environment before the build runs. &lt;code&gt;o build -p staging&lt;/code&gt; applies &lt;code&gt;profiles/staging.{tony,yaml,json}&lt;/code&gt; on top of the base env.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case: keeping configuration DRY
&lt;/h2&gt;

&lt;p&gt;The typical use case is a user organising their configuration to avoid repetition. A Kubernetes deployment directory might have dozens of manifests that share the same namespace, image registry, and version string. Without evaluation, you either duplicate these values everywhere or reach for a templating engine.&lt;/p&gt;

&lt;p&gt;With Tony evaluation, you declare the shared values once in the environment and reference them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build:
  env:
    registry: us-docker.pkg.dev/myproject/myrepo
    version: !exec "git describe --tags --always"
    namespace: production

  sources:
  - dir: ./k8s

  patches:
  - match: { kind: !or [Deployment, StatefulSet, DaemonSet] }
    patch:
      metadata:
        namespace: .[namespace]
      spec:
        template:
          spec:
            containers: !key(name)
            - name: app
              image: $[registry]/app:$[version]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The version comes from git at build time. The namespace is a plain string in the environment. The image reference is string-interpolated from multiple variables. Switch to staging by running &lt;code&gt;o build -p staging&lt;/code&gt; or &lt;code&gt;o build -- namespace=staging&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is DRY without a template language. The patches are still valid Tony/YAML documents. You can diff them, match against them, or compose them with other patches. A Helm chart is text that happens to produce YAML. A Tony build file is structured data that stays structured data throughout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case: generating config test fixtures
&lt;/h2&gt;

&lt;p&gt;The other direction is a software provider who needs to generate large volumes of configuration examples for testing. Instead of maintaining hundreds of hand-written YAML files, you define a base document and use evaluation to produce variants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build:
  env:
    test_name: basic
    replicas: 1
    image_tag: latest
    enable_monitoring: false

  sources:
  - dir: ./templates

  patches:
  - match: { kind: Deployment }
    patch:
      metadata:
        name: test-$[test_name]
        labels:
          test-case: $[test_name]
      spec:
        replicas: .[replicas]
  - match: { kind: Deployment }
    patch:
      spec:
        template:
          spec:
            containers: !key(name)
            - name: app
              image: myapp:$[image_tag]
  - match: { kind: ServiceMonitor }
    if: .[enable_monitoring]
    patch:
      metadata:
        name: test-$[test_name]-monitor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;o build -- test_name=ha replicas=3 enable_monitoring=true&lt;/code&gt; to produce an HA variant. Run it in a loop with different parameters to generate a test matrix. Each generated output is valid, diffable, matchable YAML. You can diff two test variants structurally to verify exactly what changed. You can match the output against validation patterns to catch regressions.&lt;/p&gt;

&lt;p&gt;Because evaluation composes with the other operations, the test infrastructure is the same tool as the production build infrastructure. Generate a fixture, diff it against a baseline, match it against a schema---all with &lt;code&gt;o&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not a template engine
&lt;/h2&gt;

&lt;p&gt;It's worth being explicit about what this is not. Tony evaluation is not string interpolation bolted onto YAML. It operates on the IR tree. An expression that evaluates to a number produces a number node, not a string that looks like a number. An expression that evaluates to an object produces an object node with fields and values, not a string containing YAML.&lt;/p&gt;

&lt;p&gt;Helm templates YAML as text---a Go template emits characters that happen to form valid YAML, and if your indentation is wrong, you get a parse error at a distance. Jinja2 has the same problem. Carvel's ytt avoids this by operating on YAML structure, but uses comment annotations rather than YAML's own tag mechanism, and its Starlark-based logic lives in a separate layer from the data. jsonnet solves the typing problem---its expressions produce typed values---but it's a separate language, not YAML.&lt;/p&gt;

&lt;p&gt;Tony's eval tags are none of these things. They're processed the same way as &lt;code&gt;!or&lt;/code&gt; or &lt;code&gt;!key(name)&lt;/code&gt;---a symbol in a registry, receiving an IR node, returning an IR node. The engine doesn't know or care whether a tag means "match this pattern" or "expand this expression." Everything stays in the typed IR. Evaluation doesn't break the invariant that makes match, patch, and diff work. It's all tree operations on the same tree.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>automation</category>
      <category>opensource</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Structured Matching, Patching, and Diffing Done Right</title>
      <dc:creator>Scott Cotton</dc:creator>
      <pubDate>Thu, 29 Jan 2026 20:32:58 +0000</pubDate>
      <link>https://forem.com/scott_cotton_dc9ce3e7e632/structured-matching-patching-and-diffing-done-right-22ci</link>
      <guid>https://forem.com/scott_cotton_dc9ce3e7e632/structured-matching-patching-and-diffing-done-right-22ci</guid>
      <description>&lt;p&gt;If you work with YAML or JSON configuration---Kubernetes manifests, Helm charts, IaC definitions, anything really in software ---you've probably hit the same wall. You start with a nice declarative format. Then you need to diff two versions, so you reach for &lt;code&gt;diff&lt;/code&gt;. You need to patch a field, so you reach for &lt;code&gt;yq&lt;/code&gt; or &lt;code&gt;jq&lt;/code&gt;. You need to match documents in a stream, so you reach for &lt;code&gt;grep&lt;/code&gt;. Before long, your "declarative" pipeline is held together by text munging.&lt;/p&gt;

&lt;p&gt;The higher-level tools don't escape this either. Helm templates YAML as strings. You can't diff two YAML documents as YAML---you convert to JSON first. Config-as-data systems like kpt and porch target narrow use cases--- package management, WYSIWYG authoring---because they're built on top of a hodgepodge data format model. They hide the mess rather than fix it. And the mess leaks: sooner or later you're back to &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt;, and &lt;code&gt;yq&lt;/code&gt; to glue things together.&lt;/p&gt;

&lt;p&gt;But this article isn't about building a better Helm or kpt. It's about &lt;a href="https://github.com/signadot/tony-format" rel="noopener noreferrer"&gt;getting the underlying data model right&lt;/a&gt;. What happens when matching, patching, and diffing all operate on the same typed tree representation, with the same extension mechanism, producing output that feeds back into each other?&lt;/p&gt;

&lt;p&gt;That's what Tony format does. It operates directly on the intermediate representation (IR) of structured data---a typed tree of objects, arrays, strings, numbers, booleans, and nulls. Matching, patching, and diffing are all defined as operations on this tree, not on its serialized text. And because the three operations share the same IR and the same tag-based extension mechanism, they compose in ways that text-based tools never could, and they generalize in ways config management tools never could either.&lt;/p&gt;

&lt;h2&gt;
  
  
  The IR: One Tree to Rule Them All
&lt;/h2&gt;

&lt;p&gt;At the core of Tony is &lt;code&gt;ir.Node&lt;/code&gt;, a uniform representation for structured data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Type&lt;/span&gt;    &lt;span class="n"&gt;Type&lt;/span&gt;      &lt;span class="c"&gt;// Null, Bool, Number, String, Object, Array&lt;/span&gt;
    &lt;span class="n"&gt;Fields&lt;/span&gt;  &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;   &lt;span class="c"&gt;// object keys&lt;/span&gt;
    &lt;span class="n"&gt;Values&lt;/span&gt;  &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;   &lt;span class="c"&gt;// object values or array elements&lt;/span&gt;
    &lt;span class="n"&gt;Tag&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt;    &lt;span class="c"&gt;// operation tag, e.g. "!or", "!key(name)"&lt;/span&gt;
    &lt;span class="n"&gt;String&lt;/span&gt;  &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="n"&gt;Bool&lt;/span&gt;    &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="n"&gt;Int64&lt;/span&gt;   &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;int64&lt;/span&gt;
    &lt;span class="n"&gt;Float64&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This IR can be parsed from YAML, JSON, or Tony's own format, and encoded back to any of them. Every operation---match, patch, diff---works on &lt;code&gt;*ir.Node&lt;/code&gt;. No serialization boundaries, no format-specific quirks. A patch written against YAML works identically against JSON.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Tag&lt;/code&gt; field is what makes things interesting. Tags are YAML-compatible annotations (e.g. &lt;code&gt;!or&lt;/code&gt;, &lt;code&gt;!dive&lt;/code&gt;, &lt;code&gt;!key(name)&lt;/code&gt;) that extend nodes with operational semantics. They compose via dot-separation: &lt;code&gt;!all.field.glob&lt;/code&gt; chains three operations into one. The same tag mechanism drives matching, patching, and diffing, which means the three operations speak the same language.&lt;/p&gt;

&lt;p&gt;Every tag resolves to a registered &lt;code&gt;Symbol&lt;/code&gt; that implements the &lt;code&gt;Op&lt;/code&gt; interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Op&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;OpContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="n"&gt;MatchFunc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;Patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;OpContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mf&lt;/span&gt; &lt;span class="n"&gt;MatchFunc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pf&lt;/span&gt; &lt;span class="n"&gt;PatchFunc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="n"&gt;DiffFunc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both methods are on the same interface. A single operation can define both matching and patching behavior---&lt;code&gt;!if&lt;/code&gt; tests a condition and applies a transformation in one step. The &lt;code&gt;MatchFunc&lt;/code&gt; and &lt;code&gt;PatchFunc&lt;/code&gt; callbacks enable recursive delegation back to the engine, threading context through the entire tree. New operations can be added without modifying the core engine: just register a new symbol.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Operations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Matching
&lt;/h3&gt;

&lt;p&gt;Tony's &lt;code&gt;Match(doc, pattern)&lt;/code&gt; answers a simple question: does this document satisfy this pattern? But "pattern" means something much richer than string equality.&lt;/p&gt;

&lt;p&gt;A plain match is structural. For objects, every field in the pattern must exist in the document with a matching value. For arrays, elements match positionally. For scalars, values must be equal. Crucially, the pattern is a &lt;em&gt;subset&lt;/em&gt;---the document can have fields the pattern doesn't mention.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Document&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;

&lt;span class="c1"&gt;# Pattern - matches because all specified fields are present and equal&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is already more useful than &lt;code&gt;grep 'kind: Deployment'&lt;/code&gt; because it understands nesting. But tags unlock the real power:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Match any Deployment or StatefulSet in the monitoring namespace&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!or&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Deployment&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;StatefulSet&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!glob&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;monitor*'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;!or&lt;/code&gt; matches if any alternative matches. &lt;code&gt;!glob&lt;/code&gt; matches against a glob pattern. &lt;code&gt;!and&lt;/code&gt;, &lt;code&gt;!not&lt;/code&gt;, &lt;code&gt;!has-path&lt;/code&gt;, &lt;code&gt;!type&lt;/code&gt;, &lt;code&gt;!field&lt;/code&gt;, and &lt;code&gt;!subtree&lt;/code&gt; round out the vocabulary. &lt;code&gt;!subtree&lt;/code&gt; searches the entire document depth-first. &lt;code&gt;!let&lt;/code&gt; binds variables before matching. Each callback delegates sub-matching back to the engine, so compound operations compose naturally.&lt;/p&gt;

&lt;h3&gt;
  
  
  Patching
&lt;/h3&gt;

&lt;p&gt;Tony's &lt;code&gt;Patch(doc, patch)&lt;/code&gt; transforms a document by merging a patch into it. Without tags, this works like a structural merge patch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Objects merge field-by-field. Patch fields override document fields; document fields not in the patch are preserved.&lt;/li&gt;
&lt;li&gt;Arrays merge positionally up to the shorter length, then the patch's remaining elements are appended.&lt;/li&gt;
&lt;li&gt;Scalars replace.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Document&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="c1"&gt;# Patch&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RollingUpdate&lt;/span&gt;

&lt;span class="c1"&gt;# Result&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RollingUpdate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The patch only mentions what changes. Everything else is left alone. This is the same mental model as JSON merge patch (RFC 7396), but extended to arbitrary structured data with tag support.&lt;/p&gt;

&lt;p&gt;Tags turn simple patches into a transformation language:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;!delete&lt;/code&gt;&lt;/strong&gt; removes a field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;old-annotation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!delete&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;!if&lt;/code&gt;&lt;/strong&gt; conditionally applies patches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="kt"&gt;!if&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
  &lt;span class="na"&gt;then&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;else&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="kt"&gt;!pass&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;!dive&lt;/code&gt;&lt;/strong&gt; recursively searches the document tree, applying conditional patches to every subtree. This is how you express transformations over arbitrarily nested structures without knowing the depth ahead of time.&lt;/p&gt;

&lt;p&gt;A real example: Kubernetes CRDs embed OpenAPI schemas that can be thousands of lines deep. Stripping &lt;code&gt;description&lt;/code&gt; fields from &lt;code&gt;podTemplate&lt;/code&gt; subtrees to reduce CRD size used to require a shell script. With &lt;code&gt;!dive&lt;/code&gt;, it's a patch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Match any CRD, then dive into its version schemas&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CustomResourceDefinition&lt;/span&gt;
  &lt;span class="na"&gt;patch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;versions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!dive&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;podTemplate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
        &lt;span class="na"&gt;patch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;podTemplate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!delete&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
            &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!dive&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!irtype&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
              &lt;span class="na"&gt;patch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!delete&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;!dive&lt;/code&gt; walks the tree bottom-up, applying each match/patch pair at every node. The outer dive finds any &lt;code&gt;podTemplate&lt;/code&gt; subtree anywhere in the version schema. The inner dive then strips &lt;code&gt;description&lt;/code&gt; from every object inside it. No matter how deeply nested the schema is, the patch handles it. The result: you can ship CRDs with pod templates inside without repeating megabytes of repeated descriptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;!all&lt;/code&gt;&lt;/strong&gt; applies a patch to every element of an array or every value of an object. &lt;strong&gt;&lt;code&gt;!pipe&lt;/code&gt;&lt;/strong&gt; shells out to an external command (marked unsafe, can opt out).&lt;/p&gt;

&lt;h4&gt;
  
  
  Keyed Lists
&lt;/h4&gt;

&lt;p&gt;One of the most practical features is &lt;code&gt;!key(field)&lt;/code&gt;, which tells Tony to treat an array as a map keyed by a field value. This solves the classic problem of patching Kubernetes arrays like &lt;code&gt;containers&lt;/code&gt; or &lt;code&gt;volumes&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Document&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!key(name)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:v1&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sidecar&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;proxy:v1&lt;/span&gt;

&lt;span class="c1"&gt;# Patch&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!key(name)&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp:v2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without &lt;code&gt;!key&lt;/code&gt;, array patching is positional---swap the order and your patch breaks. With &lt;code&gt;!key(name)&lt;/code&gt;, Tony matches elements by their &lt;code&gt;name&lt;/code&gt; field and merges them structurally. The &lt;code&gt;sidecar&lt;/code&gt; container is untouched because the patch doesn't mention it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Diffing
&lt;/h3&gt;

&lt;p&gt;Tony's &lt;code&gt;Diff(from, to)&lt;/code&gt; computes minimal/small structural difference between two documents. The output is itself a valid Tony document that moreover contains the common ancestors of a fine grained diff, making it easier to read than jsondiff which uses path references.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Diff&lt;/code&gt; uses tags to annotate what changed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;!replace&lt;/code&gt;&lt;/strong&gt; when types differ: &lt;code&gt;!replace { from: "old", to: "new" }&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;!delete&lt;/code&gt;&lt;/strong&gt; for removed fields or elements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;!insert&lt;/code&gt;&lt;/strong&gt; for added fields or elements&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;!strdiff&lt;/code&gt;&lt;/strong&gt; for character-level string changes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;!arraydiff&lt;/code&gt;&lt;/strong&gt; for array changes with key matching&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;!addtag&lt;/code&gt; / &lt;code&gt;!rmtag&lt;/code&gt; / &lt;code&gt;!retag&lt;/code&gt;&lt;/strong&gt; for tag-only changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fields that are identical are omitted entirely. The diff is minimal.&lt;/p&gt;

&lt;p&gt;For strings, The Go Tony &lt;code&gt;o&lt;/code&gt; tool computes character-level diffs using the diff-match-patch algorithm. But it's pragmatic: if the diff is larger than half the size of the smaller string, it falls back to a simple &lt;code&gt;!replace&lt;/code&gt;. No point in a character-level diff that's harder to read than the replacement.&lt;/p&gt;

&lt;p&gt;When arrays are tagged with &lt;code&gt;!key(field)&lt;/code&gt;, diffing matches elements by key value instead of position. Reordering doesn't produce noise---only actual additions, removals, and modifications are reported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# from&lt;/span&gt;
&lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!key(name)&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sidecar&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;proxy:v1&lt;/span&gt;

&lt;span class="c1"&gt;# to&lt;/span&gt;
&lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!key(name)&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sidecar&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;proxy:v2&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;

&lt;span class="c1"&gt;# diff - only the sidecar image changed, reordering is ignored&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sidecar&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!replace&lt;/span&gt;
    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;proxy:v1&lt;/span&gt;
    &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;proxy:v2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For plain arrays (without &lt;code&gt;!key&lt;/code&gt;), &lt;code&gt;!arraydiff&lt;/code&gt; uses an abstracted longest-common-subsequence to produce a positional diff with &lt;code&gt;!insert&lt;/code&gt; and &lt;code&gt;!delete&lt;/code&gt; entries---and both the result and the per-item differences are valid patches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# from&lt;/span&gt;
&lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;a&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;b&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;c&lt;/span&gt;

&lt;span class="c1"&gt;# to&lt;/span&gt;
&lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;a&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;x&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;c&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;d&lt;/span&gt;

&lt;span class="c1"&gt;# diff&lt;/span&gt;
&lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!arraydiff&lt;/span&gt;
  &lt;span class="na"&gt;1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!replace&lt;/span&gt;
    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;b&lt;/span&gt;
    &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;x&lt;/span&gt;
  &lt;span class="na"&gt;3&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!insert&lt;/span&gt; &lt;span class="s"&gt;d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Diffs are reversible. &lt;code&gt;libdiff.Reverse(diff)&lt;/code&gt; produces a diff that, when applied to &lt;code&gt;to&lt;/code&gt;, yields &lt;code&gt;from&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;reversed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;libdiff&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;// Patch(b, reversed) == a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reversal logic is straightforward because the tags carry complete information: &lt;code&gt;!delete&lt;/code&gt; becomes &lt;code&gt;!insert&lt;/code&gt; and vice versa, &lt;code&gt;!replace&lt;/code&gt; swaps its &lt;code&gt;from&lt;/code&gt; and &lt;code&gt;to&lt;/code&gt; fields, &lt;code&gt;!retag(x,y)&lt;/code&gt; becomes &lt;code&gt;!retag(y,x)&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Composition
&lt;/h2&gt;

&lt;p&gt;The real payoff is that these three operations form a closed loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A diff is a valid patch.&lt;/strong&gt; The same tags that annotate changes (&lt;code&gt;!delete&lt;/code&gt;, &lt;code&gt;!insert&lt;/code&gt;, &lt;code&gt;!replace&lt;/code&gt;) are recognized by the patch engine. &lt;code&gt;Diff(a, b)&lt;/code&gt; produces output that you can pass directly to &lt;code&gt;Patch(a, diff)&lt;/code&gt; to get &lt;code&gt;b&lt;/code&gt;. Reverse the diff and apply it to &lt;code&gt;b&lt;/code&gt; to get &lt;code&gt;a&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Matching composes with patching.&lt;/strong&gt; &lt;code&gt;!if&lt;/code&gt; uses match semantics in its condition and patch semantics in its body. &lt;code&gt;!dive&lt;/code&gt; matches to find subtrees and patches to transform them. The &lt;code&gt;Op&lt;/code&gt; interface makes this explicit---every operation is both a matcher and a patcher, and the engine threads recursive callbacks for both through the entire tree.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Diffs compose with matching.&lt;/strong&gt; Because a diff is structured Tony data, you can match against it, filter it, or patch it before applying it. A diff isn't an opaque blob you feed to &lt;code&gt;patch&lt;/code&gt;---it's a document you can inspect and transform with the same tools you use on any other document.&lt;/p&gt;

&lt;p&gt;This isn't three systems bolted together. It's one system and one structure-preserving format with three entry points. Match, patch, and diff are perspectives on the same tree-walking, tag-dispatching engine. The &lt;code&gt;Op&lt;/code&gt; interface, the IR, and the tag registry are shared all the way down.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Three operations. One IR. One tag system. Matching, patching, and diffing are the same operation viewed from different angles, and the model makes that explicit. That's the whole idea.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>devops</category>
      <category>kubernetes</category>
      <category>tooling</category>
    </item>
    <item>
      <title>All I have to say is !</title>
      <dc:creator>Scott Cotton</dc:creator>
      <pubDate>Thu, 08 Jan 2026 08:44:06 +0000</pubDate>
      <link>https://forem.com/scott_cotton_dc9ce3e7e632/all-i-have-to-say-is--4ce3</link>
      <guid>https://forem.com/scott_cotton_dc9ce3e7e632/all-i-have-to-say-is--4ce3</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/scott_cotton_dc9ce3e7e632" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3699965%2F5404d330-9b91-49ab-be6a-6d1b6d033917.jpg" alt="scott_cotton_dc9ce3e7e632"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/scott_cotton_dc9ce3e7e632/one-source-two-outputs-generating-k8s-yaml-and-helm-charts-with-tony-format-30h6" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;One Source, Two Outputs: Generating K8s YAML and Helm Charts with Tony Format&lt;/h2&gt;
      &lt;h3&gt;Scott Cotton ・ Jan 8&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>One Source, Two Outputs: Generating K8s YAML and Helm Charts with Tony Format</title>
      <dc:creator>Scott Cotton</dc:creator>
      <pubDate>Thu, 08 Jan 2026 08:42:33 +0000</pubDate>
      <link>https://forem.com/scott_cotton_dc9ce3e7e632/one-source-two-outputs-generating-k8s-yaml-and-helm-charts-with-tony-format-30h6</link>
      <guid>https://forem.com/scott_cotton_dc9ce3e7e632/one-source-two-outputs-generating-k8s-yaml-and-helm-charts-with-tony-format-30h6</guid>
      <description>&lt;p&gt;We use controller-gen to generate CRDs and base manifests. These YAML files are our source of truth—used in CI, deployed to dev clusters like minikube, and tested internally. But customers want Helm charts.&lt;/p&gt;

&lt;p&gt;This creates an uncomfortable constraint: &lt;strong&gt;Helm cannot be our source of truth&lt;/strong&gt; because controller-gen outputs YAML. We need to generate Helm charts &lt;em&gt;from&lt;/em&gt; YAML, not the other way around.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Impedance Mismatch
&lt;/h2&gt;

&lt;p&gt;Helm has a fundamental problem: you're editing YAML, but you're not.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;manager&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.image.repository&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;&lt;span class="s"&gt;:{{ .Values.image.tag }}&lt;/span&gt;
  &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- if .Values.resources&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.resources | toYaml | nindent 4&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- end&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This isn't valid YAML. Your IDE doesn't understand it. YAML validators reject it. Kustomize can't patch it. Every tool in the Kubernetes ecosystem expects YAML, but Helm templates are a hybrid format that nothing else can process.&lt;/p&gt;

&lt;p&gt;This means you can't use kustomize to customize Helm charts. You can't use Helm as a source and generate plain YAML with structural patches. You're stuck maintaining two parallel worlds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our Old Workflow: Death by a Thousand Hacks
&lt;/h2&gt;

&lt;p&gt;Our previous solution was a pipeline of custom tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="err"&gt;kustomize&lt;/span&gt; &lt;span class="err"&gt;build&lt;/span&gt; &lt;span class="err"&gt;deploy/operator/overlays/helm&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;
  &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="err"&gt;go&lt;/span&gt; &lt;span class="err"&gt;run&lt;/span&gt; &lt;span class="err"&gt;./hack/fix-cm&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;
  &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="err"&gt;go&lt;/span&gt; &lt;span class="err"&gt;run&lt;/span&gt; &lt;span class="err"&gt;./hack/templatize.go&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;
    &lt;span class="err"&gt;-charts-dir&lt;/span&gt; &lt;span class="err"&gt;$(CHARTS_DIR)&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;
    &lt;span class="err"&gt;-templates-dir&lt;/span&gt; &lt;span class="err"&gt;templates&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Each piece solved a problem that shouldn't exist.&lt;/p&gt;

&lt;h3&gt;
  
  
  hack/fix-cm.go: Working Around Kustomize's ConfigMap Limitations
&lt;/h3&gt;

&lt;p&gt;Kustomize can't patch individual fields inside ConfigMap &lt;code&gt;data&lt;/code&gt; values. Our workaround: store structured data in ConfigMaps as actual YAML structures (invalid for a real ConfigMap), let kustomize patch them, then convert back to strings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Input: invalid ConfigMap with structured data field&lt;/span&gt;
&lt;span class="c"&gt;// data:&lt;/span&gt;
&lt;span class="c"&gt;//   config:&lt;/span&gt;
&lt;span class="c"&gt;//     x: xy&lt;/span&gt;
&lt;span class="c"&gt;//     f: v&lt;/span&gt;
&lt;span class="c"&gt;//&lt;/span&gt;
&lt;span class="c"&gt;// Output: valid ConfigMap with string field&lt;/span&gt;
&lt;span class="c"&gt;// data:&lt;/span&gt;
&lt;span class="c"&gt;//   config: |&lt;/span&gt;
&lt;span class="c"&gt;//     x: xy&lt;/span&gt;
&lt;span class="c"&gt;//     f: v&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;A 100-line Go program to work around a tool limitation.&lt;/p&gt;

&lt;h3&gt;
  
  
  hack/templatize.go: 450 Lines of Regex
&lt;/h3&gt;

&lt;p&gt;Since kustomize rejects invalid YAML, we couldn't put Helm templates in our source files. Instead, we invented a shadow language of annotations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# In kustomize overlay&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;templatize.signadot.com/range-map&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.Values.commonLabels&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;templatize.signadot.com/include-if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;not .Values.disableAgent&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Then &lt;code&gt;templatize.go&lt;/code&gt; scans the output line-by-line with regex, looking for these annotations and rewriting them into Helm templates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;directiveExp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;regexp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MustCompile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`^(\\s*)templatize\\.signadot\\.com/([^:]+):\\s*(.+)$`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;signadotImageExp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;regexp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MustCompile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`^(\\s*-?\\s*)(image|value):\\s*signadot/([a-z-]+):(.+)$`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;helmValuesExp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;regexp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MustCompile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`^(.*)helm-values.signadot.com/([a-zA-Z.][a-zA-Z0-9_.]*)(\\?(.*))?$`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Every image reference, every conditional include, every values injection—all handled by pattern matching on text that happens to look like YAML.&lt;/p&gt;

&lt;p&gt;This almost worked. Every change required understanding both the kustomize overlay system &lt;em&gt;and&lt;/em&gt; the templatize conventions. Adding a new configurable field meant editing multiple files and hoping the regex would match correctly.&lt;/p&gt;

&lt;p&gt;But the real killer was what happened when regex couldn't express a transformation. The escape hatch: &lt;strong&gt;git unified diffs stored as source code&lt;/strong&gt;, applied against generated output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git b/templates/agent-deployment.yaml a/templates/agent-deployment.yaml
&lt;/span&gt;&lt;span class="p"&gt;@@ -31,6 +35,15 @@&lt;/span&gt; spec:
         {{- range $key, $val := .Values.podAnnotations }}
         {{ $key | quote }}: {{ $val | quote }}
         {{- end }}
&lt;span class="gi"&gt;+        {{- if $linkerdEnabled }}
+        {{- range $key, $val := .Values.linkerd.operator.podAnnotations }}
+        {{ $key | quote }}: {{ $val | quote }}
+        {{- end }}
+        {{- end }}
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These patches target files that don't exist—they're generated mid-pipeline. If templatize output shifts by one line (a new annotation, a reordered field), the patch context no longer matches and the build fails. You're debugging line number mismatches in diffs against fictional files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tony's Solution: Valid Documents All the Way Down
&lt;/h2&gt;

&lt;p&gt;Tony format solves this by letting you embed raw text in structurally valid documents using merge keys (&lt;code&gt;&amp;lt;&amp;lt;:&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{-&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;if&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.Values.resources&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}'&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.Values.resources&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;toYaml&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;|&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;nindent&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;4&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}'&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{-&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;else&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}'&lt;/span&gt;
  &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;512Mi"&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{-&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;end&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}'&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This is valid Tony. It parses. It patches. And when you run &lt;code&gt;o build&lt;/code&gt; with the expand merge keys option &lt;code&gt;-x&lt;/code&gt;, the merge keys expand to raw text at the correct indentation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- if .Values.resources&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;span class="pi"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;.Values.resources | toYaml | nindent 4&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- else&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;
  &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;512Mi"&lt;/span&gt;
&lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- end&lt;/span&gt; &lt;span class="pi"&gt;}}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;For simple value substitution, &lt;code&gt;!literal&lt;/code&gt; preserves template syntax:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!literal&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.Values.image.repository&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}:{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.Values.image.tag&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}'&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This guarantees that the output ends up like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
  &lt;span class="s"&gt;{{ .Values.image.repository }}:{{ .Values.image.tag }}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;preserving the quoting conventions inside templates which are needed for Helm.&lt;/p&gt;

&lt;h3&gt;
  
  
  The New Workflow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deploy/
  sdctl-operator/
    build.tony              # Internal deployment build
    source/                 # Deployments, services
    crd/bases/              # controller-gen output
  charts/sdctl-operator/
    build.tony              # Helm chart build
    patches/
      images.tony           # Templatize images
      resources.tony        # Templatize resources

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

&lt;/div&gt;



&lt;p&gt;The Helm chart build sources from the internal deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;destDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;../../../dist/charts/signadot/sdctl-operator/templates&lt;/span&gt;

  &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;o&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;b&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;../../sdctl-operator"&lt;/span&gt;  &lt;span class="c1"&gt;# Reuse the same base&lt;/span&gt;
    &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yaml&lt;/span&gt;

  &lt;span class="na"&gt;patches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;patches/images.tony&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;patches/resources.tony&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The image patch uses &lt;code&gt;!literal&lt;/code&gt; and &lt;code&gt;!key(name)&lt;/code&gt; for structural merging:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sdctl-controller-manager&lt;/span&gt;
  &lt;span class="na"&gt;patch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!key(name)&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;manager&lt;/span&gt;
            &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!literal&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;include&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"valuesDefault"&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;(list&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.Values&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"signadot/sdctl-controller-manager:latest"&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"controllerManager"&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"image")&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}'&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;CI becomes straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Deploy to minikube&lt;/span&gt;
o build deploy/sdctl-operator/ | kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; -

&lt;span class="c"&gt;# Build Helm chart for customers&lt;/span&gt;
o build &lt;span class="nt"&gt;-x&lt;/span&gt; deploy/charts/sdctl-operator/

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

&lt;/div&gt;



&lt;p&gt;controller-gen stays the source of truth. No regex. No annotation hacks. No ConfigMap fixups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Actually Works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Generic Tools Beat Domain-Specific Ones
&lt;/h3&gt;

&lt;p&gt;Kustomize's philosophy is that it &lt;em&gt;understands&lt;/em&gt; Kubernetes. It knows that &lt;code&gt;containers&lt;/code&gt; merges by &lt;code&gt;name&lt;/code&gt; because the upstream Go struct has &lt;code&gt;patchMergeKey:"name"&lt;/code&gt;. This sounds helpful until you realize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can't change merge behavior for fields Kubernetes got wrong&lt;/li&gt;
&lt;li&gt;You can't use kustomize on anything that isn't a K8s resource&lt;/li&gt;
&lt;li&gt;Every K8s API change potentially changes your patch semantics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tony takes the opposite approach: it knows nothing about Kubernetes. It's a generic tool for structured data that happens to work beautifully on K8s manifests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!key(name)&lt;/span&gt;  &lt;span class="c1"&gt;# You declare merge semantics, not upstream Go structs&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This works on any array, in any document. Patch your Helm &lt;code&gt;values.yaml&lt;/code&gt;. Patch CI configs. Patch Terraform variables. The same tool, the same syntax, the same mental model—without being locked into one ecosystem's idea of how merging should work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Structural Matching
&lt;/h3&gt;

&lt;p&gt;Kustomize requires you to know resource names upfront. Tony matches by structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;myapp&lt;/span&gt;
  &lt;span class="na"&gt;patch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Match expressions compose with &lt;code&gt;!or&lt;/code&gt;, &lt;code&gt;!and&lt;/code&gt;, and &lt;code&gt;!not&lt;/code&gt;. Apply a patch to all ConfigMaps and Secrets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!or&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;ConfigMap&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Secret&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;patch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;managed-by&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tony&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conditionals Without Directory Explosion
&lt;/h3&gt;

&lt;p&gt;Kustomize needs separate overlay directories for each variant. Tony uses expressions,&lt;br&gt;
over a substitution environment similar to Helm's values.yaml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.[&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;environment&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"prod"&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;]'&lt;/span&gt;
  &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;Deployment&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;patch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;5&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Moreover Tony has profiles which are patches to the substition environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking Forward
&lt;/h2&gt;

&lt;p&gt;Migrating to Tony gave us something we didn't have before: a coherent pipeline we actually understand. No more debugging regex transforms or tracing through three layers of kustomize overlays to figure out why an annotation didn't convert properly, or constructing text patches against fictional targets as source code.&lt;/p&gt;

&lt;p&gt;But this is just the start. The &lt;code&gt;o build&lt;/code&gt; model—declarative builds with sources, patches, and environment overrides—opens up possibilities we're actively exploring:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remote builds.&lt;/strong&gt; Today &lt;code&gt;o build&lt;/code&gt; runs locally. We're adding support for remote sources and build execution, so your &lt;code&gt;build.tony&lt;/code&gt; can reference artifacts from CI, pull base manifests from registries, or execute builds in controlled environments. The same declarative format, but with the dependency resolution happening across network boundaries.&lt;/p&gt;

&lt;p&gt;The goal: treat Kubernetes manifests with the same rigor we treat code. Reproducible builds from declared inputs, whether those inputs are local files, controller-gen output, or artifacts from a release pipeline.&lt;/p&gt;

&lt;p&gt;If you're fighting similar battles with Helm generation, give Tony a look: &lt;a href="https://github.com/signadot/tony-format" rel="noopener noreferrer"&gt;github.com/signadot/tony-format&lt;/a&gt;&lt;/p&gt;

</description>
      <category>automation</category>
      <category>kubernetes</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
