<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Flux – Use Cases</title><link>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/</link><description>Recent content in Use Cases on Flux</description><generator>Hugo -- gohugo.io</generator><language>en</language><atom:link href="https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/index.xml" rel="self" type="application/rss+xml"/><item><title>Flux: Running pre and post-deployment jobs with Flux</title><link>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/running-jobs/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/running-jobs/</guid><description>
&lt;p>Additional considerations have to be made when managing Kubernetes Jobs with Flux.
By default, if you were to have Flux reconcile a Job resource,
it would apply it once to the cluster, the Job would create a Pod that can either error or run to completion.
Attempting to update the Job manifest after it has been applied to the cluster will not be allowed, as changes to the
Job &lt;code>spec.Completions&lt;/code>, &lt;code>spec.Selector&lt;/code> and &lt;code>spec.Template&lt;/code> are not permitted by the Kubernetes API.
To be able to update a Kubernetes Job, the Job has to be recreated by first being
removed and then reapplied to the cluster.&lt;/p>
&lt;h2 id="repository-structure">Repository structure&lt;/h2>
&lt;p>A typical use case for running Kubernetes Jobs with Flux is to implement pre-deployment tasks
for e.g. database scheme migration and post-deployment jobs (like cache refresh).&lt;/p>
&lt;p>This requires separate
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/components/kustomize/kustomizations/">Flux Kustomization&lt;/a> resources
that depend on each other: one for running the pre-deployment Jobs,
one to deploy the application, and a 3rd one for running the post-deployment Jobs.&lt;/p>
&lt;p>Example of an application configuration repository:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>├── pre-deploy
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│   └── migration.job.yaml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>├── deploy
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│   ├── deployment.yaml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│   ├── ingress.yaml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│   └── service.yaml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>├── post-deploy
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>│   └── cache.job.yaml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>└── flux
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ├── pre-deploy.yaml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ├── deploy.yaml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> └── post-deploy.yaml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="configure-the-deployment-pipeline">Configure the deployment pipeline&lt;/h2>
&lt;p>Given a Job in the path &lt;code>./pre-deploy/migration.job.yaml&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>batch/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Job&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>db-migration&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">template&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">restartPolicy&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Never&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">containers&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>migration&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">image&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io/org/my-app:v1.0.0&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">command&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- sh&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- -c&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- echo &amp;#34;starting db migration&amp;#34;&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And a Flux Kustomization that reconciles it at &lt;code>./flux/pre-deploy.yaml&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kustomize.toolkit.fluxcd.io/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Kustomization&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>app-pre-deploy&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sourceRef&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>GitRepository&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>my-app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">path&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;./pre-deploy/&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">interval&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>60m&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">timeout&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>5m&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">prune&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">wait&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">force&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Setting &lt;code>spec.force&lt;/code> to &lt;code>true&lt;/code> will make Flux recreate the Job when any immutable field is changed,
forcing the Job to run every time the container image tag changes.
Setting &lt;code>spec.wait&lt;/code> to &lt;code>true&lt;/code> makes Flux wait for the Job to complete
before it is considered ready.&lt;/p>
&lt;p>To deploy the application after the migration job,
we define a Flux Kustomization that depends on the migration one.&lt;/p>
&lt;p>Example of &lt;code>./flux/deploy.yaml&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kustomize.toolkit.fluxcd.io/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Kustomization&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>app-deploy&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">dependsOn&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>app-pre-deploy&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sourceRef&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>GitRepository&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>my-app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">path&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;./deploy/&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">interval&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>60m&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">timeout&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>5m&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">prune&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">wait&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This means that the &lt;code>app-deploy&lt;/code> Kustomization will wait until all the Jobs in &lt;code>app-pre-deploy&lt;/code> run to completion.
If the Job fails, the app changes will not be applied by the &lt;code>app-deploy&lt;/code> Kustomization.&lt;/p>
&lt;p>And finally we can define a Flux Kustomization that depends on &lt;code>app-deploy&lt;/code> to run Kubernetes Jobs after the
application was upgraded.&lt;/p>
&lt;p>Example of &lt;code>./flux/post-deploy.yaml&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kustomize.toolkit.fluxcd.io/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Kustomization&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>app-post-deploy&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">dependsOn&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>app-deploy&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sourceRef&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>GitRepository&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>my-app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">path&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;./post-deploy/&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">interval&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>60m&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">timeout&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>5m&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">prune&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">wait&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">force&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This configuration works best when the Jobs are using the same image and tag as the application being deployed.
When a new version of the application is deployed, the image tags are updated.
The update of the image tag will force a recreation of the Jobs.
The application will be updated after the pre-deployment Jobs have run successfully, and
the post-deployment Jobs will execute only if the app rolling update has completed.&lt;/p></description></item><item><title>Flux: Karmada + Flux</title><link>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/karmada/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/karmada/</guid><description>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Disclaimer&lt;/h4>
&lt;p>Note that this guide is not for doing GitOps, but for managing Helm releases for applications among multiple clusters.&lt;/p>
&lt;p>Also note that this guide needs review in consideration of Flux v2.0.0, and likely needs to be refreshed.&lt;/p>
&lt;p>Expect this doc to either be archived soon, or to receive some overhaul.&lt;/p>
&lt;/div>
&lt;h2 id="background">Background&lt;/h2>
&lt;p>
&lt;a href="https://github.com/karmada-io/karmada" target="_blank">Karmada&lt;/a> is a Kubernetes management system that enables you to run your cloud-native applications across multiple Kubernetes clusters and clouds, with no changes to your applications.
By speaking Kubernetes-native APIs and providing advanced scheduling capabilities, Karmada enables truly open, multi-cloud Kubernetes.
With Karmada&amp;rsquo;s centralized multi-cloud management, users can easily distribute and manage Helm releases in multiple clusters based on powerful Flux APIs.&lt;/p>
&lt;h2 id="karmada-setup">Karmada Setup&lt;/h2>
&lt;p>Steps described in this document have been tested on Karmada 1.0, 1.1 and 1.2.
To start up Karmada, you can refer to
&lt;a href="https://github.com/karmada-io/karmada/blob/master/docs/installation/installation.md" target="_blank">here&lt;/a>.
If you just want to try Karmada, we recommend building a development environment by &lt;code>hack/local-up-karmada.sh&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>git clone https://github.com/karmada-io/karmada
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">cd&lt;/span> karmada
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>hack/local-up-karmada.sh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>After that, you will start a Kubernetes cluster by kind to run the Karmada control plane and create member clusters managed by Karmada.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>kubectl get clusters --kubeconfig ~/.kube/karmada.config
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can use the command above to check registered clusters, and you will get similar output as follows:&lt;/p>
&lt;pre tabindex="0">&lt;code>NAME VERSION MODE READY AGE
member1 v1.23.4 Push True 7m38s
member2 v1.23.4 Push True 7m35s
member3 v1.23.4 Pull True 7m27s
&lt;/code>&lt;/pre>&lt;h3 id="flux-installation">Flux Installation&lt;/h3>
&lt;p>In Karmada control plane, you need to install Flux crds but do not need controllers to reconcile them. They are treated as resource templates, not specific resource instances.
Based on work API
&lt;a href="https://github.com/kubernetes-sigs/work-api" target="_blank">here&lt;/a>, they will be encapsulated as a work object deliverd to member clusters and reconciled by Flux controllers in member clusters finally.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>kubectl apply -k github.com/fluxcd/flux2/manifests/crds?ref&lt;span style="color:#666">=&lt;/span>main --kubeconfig ~/.kube/karmada.config
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For testing purposes, we&amp;rsquo;ll install Flux on member clusters without storing its manifests in a Git repository:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>flux install --kubeconfig ~/.kube/members.config --context member1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux install --kubeconfig ~/.kube/members.config --context member2
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Please refer to the documentations
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/installation/">here&lt;/a> for more ways to set up Flux in details.&lt;/p>
&lt;div class="alert alert-info" role="alert">
&lt;h4 class="alert-heading">Tip&lt;/h4>
If you want to manage Helm releases across your fleet of clusters, Flux must be installed on each cluster.
&lt;/div>
&lt;h2 id="helm-release-propagation">Helm release propagation&lt;/h2>
&lt;p>If you want to propagate Helm releases for your apps to member clusters, you can refer to the guide below.&lt;/p>
&lt;ol>
&lt;li>Define a Flux &lt;code>HelmRepository&lt;/code> and a &lt;code>HelmRelease&lt;/code> manifest in Karmada Control plane. They will serve as resource templates.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>source.toolkit.fluxcd.io/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRepository&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">interval&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>1m&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">url&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>https://stefanprodan.github.io/podinfo &lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#0e84b5;font-weight:bold">---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>helm.toolkit.fluxcd.io/v2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRelease&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">interval&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>5m&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">chart&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">chart&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">version&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">5.0.3&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sourceRef&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRepository&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>Define a Karmada &lt;code>PropagationPolicy&lt;/code> that will propagate them to member clusters:&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>policy.karmada.io/v1alpha1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>PropagationPolicy&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>helm-repo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">resourceSelectors&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>source.toolkit.fluxcd.io/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRepository&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">placement&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">clusterAffinity&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">clusterNames&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- member1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- member2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#0e84b5;font-weight:bold">---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>policy.karmada.io/v1alpha1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>PropagationPolicy&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>helm-release&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">resourceSelectors&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>helm.toolkit.fluxcd.io/v2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRelease&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">placement&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">clusterAffinity&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">clusterNames&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- member1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- member2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The above configuration is for propagating the Flux objects to member1 and member2 clusters.&lt;/p>
&lt;ol start="3">
&lt;li>Apply those manifests to the Karmada-apiserver:&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>kubectl apply -f ../helm/ --kubeconfig ~/.kube/karmada.config
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The output is similar to:&lt;/p>
&lt;pre tabindex="0">&lt;code>helmrelease.helm.toolkit.fluxcd.io/podinfo created
helmrepository.source.toolkit.fluxcd.io/podinfo created
propagationpolicy.policy.karmada.io/helm-release created
propagationpolicy.policy.karmada.io/helm-repo created
&lt;/code>&lt;/pre>&lt;ol start="4">
&lt;li>Switch to the distributed cluster and verify:&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>helm --kubeconfig ~/.kube/members.config --kube-context member1 list
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The output is similar to:&lt;/p>
&lt;pre tabindex="0">&lt;code>NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
podinfo default 1 2022-05-27 01:44:35.24229175 +0000 UTC deployed podinfo-5.0.3 5.0.3
&lt;/code>&lt;/pre>&lt;p>Based on Karmada&amp;rsquo;s propagation policy, you can schedule Helm releases to your desired cluster flexibly, just like Kubernetes scheduling Pods to the desired node.&lt;/p>
&lt;h2 id="customize-the-helm-release-for-specific-clusters">Customize the Helm release for specific clusters&lt;/h2>
&lt;p>The example above shows how to distribute the same Helm release to multiple clusters in Karmada. Besides, you can use Karmada&amp;rsquo;s OverridePolicy to customize applications for specific clusters.
For example, If you just want to change replicas in member1, you can refer to the overridePolicy below.&lt;/p>
&lt;ol>
&lt;li>Define a Karmada &lt;code>OverridePolicy&lt;/code>:&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>policy.karmada.io/v1alpha1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>OverridePolicy&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>example-override&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>default&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">resourceSelectors&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>helm.toolkit.fluxcd.io/v2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRelease&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">overrideRules&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">targetCluster&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">clusterNames&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- member1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">overriders&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">plaintext&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">path&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;/spec/values&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">operator&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>add&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">value&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">replicaCount&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">2&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ol start="2">
&lt;li>Apply the manifests to the Karmada-apiserver:&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>kubectl apply -f example-override.yaml --kubeconfig ~/.kube/karmada.config
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The output is similar to:&lt;/p>
&lt;pre tabindex="0">&lt;code>overridepolicy.policy.karmada.io/example-override configured
&lt;/code>&lt;/pre>&lt;ol start="3">
&lt;li>After applying the above policy in Karmada control plane, you will find that replicas in member1 has changed to 2, but those in member2 keep the same.&lt;/li>
&lt;/ol>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>kubectl --kubeconfig ~/.kube/members.config --context member1 get po
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The output is similar to:&lt;/p>
&lt;pre tabindex="0">&lt;code>NAME READY STATUS RESTARTS AGE
podinfo-68979685bc-6wz6s 1/1 Running 0 6m28s
podinfo-68979685bc-dz9f6 1/1 Running 0 7m42s
&lt;/code>&lt;/pre></description></item><item><title>Flux: Flux for Helm Users</title><link>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/helm/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/helm/</guid><description>
&lt;p>Welcome Helm users!
We think Flux&amp;rsquo;s Helm Controller is the best way to do Helm according to GitOps principles,
and we&amp;rsquo;re dedicated to doing what we can to help you feel the same way.&lt;/p>
&lt;h2 id="what-does-flux-add-to-helm">What Does Flux add to Helm?&lt;/h2>
&lt;p>Helm 3 was designed with both a client and an SDK, but no running software agents.
This architecture intended anything outside of the client scope to be addressed by other tools in the ecosystem,
which could then make use of Helm&amp;rsquo;s SDK.&lt;/p>
&lt;p>Built on Kubernetes controller-runtime, Flux&amp;rsquo;s Helm Controller is an example of a mature software
agent that uses Helm&amp;rsquo;s SDK to full effect.&lt;/p>
&lt;p>Flux&amp;rsquo;s biggest addition to Helm is a structured declaration layer for your releases that
automatically gets reconciled to your cluster based on your configured rules:&lt;/p>
&lt;ul>
&lt;li>While the Helm client commands let you imperatively do things&lt;/li>
&lt;li>Flux Helm Custom Resources let you declare what you want the Helm SDK to do automatically&lt;/li>
&lt;/ul>
&lt;p>Additional benefits Flux adds to Helm include:&lt;/p>
&lt;ul>
&lt;li>Managing / structuring multiple environments&lt;/li>
&lt;li>A control loop, with configurable retry logic&lt;/li>
&lt;li>Automated drift detection between the desired and actual state of your operations&lt;/li>
&lt;li>Automated responses to that drift, including reconciliation, notifications, and unified logging&lt;/li>
&lt;/ul>
&lt;h2 id="getting-started">Getting Started&lt;/h2>
&lt;p>The simplest way to explain is by example.
Lets translate imperative Helm commands to Flux Helm Controller Custom Resources:&lt;/p>
&lt;p>Helm client:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>helm repo add traefik https://helm.traefik.io/traefik
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>helm install my-traefik traefik/traefik &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> --version 9.18.2 &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> --namespace traefik
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Flux client:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>flux create &lt;span style="color:#007020">source&lt;/span> helm traefik --url https://helm.traefik.io/traefik --namespace traefik
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux create helmrelease my-traefik --chart traefik &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> --source HelmRepository/traefik &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> --chart-version 9.18.2 &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> --namespace traefik
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The main difference is that the Flux client will not imperatively create resources in the cluster.
Instead, these commands create Custom Resource &lt;em>files&lt;/em>, which are committed to version control
as instructions only (note: you may use the &lt;code>--export&lt;/code> flag to manage any file edits with
finer grained control before pushing to version control).
Separately, the Flux Helm Controller automatically reconciles these instructions
with the running state of your cluster based on your configured rules.&lt;/p>
&lt;p>Let&amp;rsquo;s check out what the Custom Resource files look like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># /flux/boot/traefik/helmrepo.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>source.toolkit.fluxcd.io/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRepository&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>traefik&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>traefik&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">interval&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>1m0s&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">url&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>https://helm.traefik.io/traefik&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># /flux/boot/traefik/helmrelease.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>helm.toolkit.fluxcd.io/v2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRelease&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>my-traefik&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>traefik&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">chart&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">chart&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>traefik&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sourceRef&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRepository&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>traefik&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">version&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">9.18.2&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">interval&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>1m0s&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once these are applied to your cluster, the Flux Helm Controller automatically
uses the Helm SDK to do your bidding according to the rules you&amp;rsquo;ve set.&lt;/p>
&lt;p>Why is this important?
If you or your team has ever collaborated with multiple engineers on one or more apps,
and/or in more than one namespace or cluster, you probably have a good idea of how declarative,
automatic reconciliation can help solve common problems.
If not, or either way, you may want to check out this
&lt;a href="https://youtu.be/r-upyR-cfDY" target="_blank">short introduction to GitOps&lt;/a>.&lt;/p>
&lt;h2 id="customizing-your-release">Customizing Your Release&lt;/h2>
&lt;p>While Helm charts are usually installable using default configurations,
users will often customize charts with their preferred configuration
by
&lt;a href="https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing" target="_blank">overriding the default values&lt;/a>.
The Helm client allows this by imperatively specifying override values with &lt;code>--set&lt;/code> on the command line,
and in additional &lt;code>--values&lt;/code> files. For example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>helm install my-traefik traefik/traefik --set service.type&lt;span style="color:#666">=&lt;/span>ClusterIP
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>and&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-sh" data-lang="sh">&lt;span style="display:flex;">&lt;span>helm install my-traefik traefik/traefik --values ci/kind-values.yaml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>where &lt;code>ci/kind-values.yaml&lt;/code> contains:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">service&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">type&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ClusterIP&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Flux Helm Controller allows these same YAML values overrides on the &lt;code>HelmRelease&lt;/code> CRD.
These can be declared directly in &lt;code>spec.values&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">values&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">service&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">type&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ClusterIP&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>and defined in &lt;code>spec.valuesFrom&lt;/code> as a list of &lt;code>ConfigMap&lt;/code> and &lt;code>Secret&lt;/code> resources from which to draw values,
allowing reusability and/or greater security.
See &lt;code>HelmRelease&lt;/code> CRD
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/components/helm/helmreleases/#values-overrides">values overrides&lt;/a>
documentation for the latest spec.&lt;/p>
&lt;h2 id="managing-secrets-and-configmaps">Managing Secrets and ConfigMaps&lt;/h2>
&lt;p>You may manage these &lt;code>ConfigMap&lt;/code> and &lt;code>Secret&lt;/code> resources any way you wish,
but there are several benefits to managing these with the Flux Kustomize Controller.&lt;/p>
&lt;p>It is fairly straightforward to use Kustomize &lt;code>configMapGenerator&lt;/code>
to
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/helmreleases/#refer-to-values-in-configmaps-generated-with-kustomize">trigger a Helm release upgrade every time the encoded values change&lt;/a>.
This common use case currently solveable in Helm
by
&lt;a href="https://helm.sh/docs/howto/charts_tips_and_tricks/#automatically-roll-deployments" target="_blank">adding specially crafted annotations&lt;/a>
to a chart. The Flux Kustomize Controller method allows you to accomplish this
on any chart without additional templated annotations.&lt;/p>
&lt;p>You may also use Kustomize Controller
built-in
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/components/kustomize/kustomizations/#decryption">Mozilla SOPS integration&lt;/a>
to securely manage your encrypted secrets stored in git.
See the
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/mozilla-sops/">Flux SOPS guide&lt;/a> for step-by-step instructions through various use cases.&lt;/p>
&lt;h2 id="automatic-release-upgrades">Automatic Release Upgrades&lt;/h2>
&lt;p>If you want Helm Controller to automatically upgrade your releases when a new chart version is available
in the release&amp;rsquo;s referenced &lt;code>HelmRepository&lt;/code>,
you may specify a SemVer range (i.e. &lt;code>&amp;gt;=4.0.0 &amp;lt;5.0.0&lt;/code>) instead of a fixed version.&lt;/p>
&lt;p>This is useful if your release should use a fixed MAJOR chart version,
but want the latest MINOR or PATCH versions as they become available.&lt;/p>
&lt;p>For full SemVer range syntax,
see &lt;code>Masterminds/semver&lt;/code>
&lt;a href="https://github.com/Masterminds/semver/blob/master/README.md#checking-version-constraints" target="_blank">Checking Version Constraints&lt;/a>
documentation.&lt;/p>
&lt;h2 id="automatic-uninstalls-and-rollback">Automatic Uninstalls and Rollback&lt;/h2>
&lt;p>The Helm Controller offers an extensive set of configuration options to remediate when a Helm release fails,
using
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/components/helm/api/v2/#helm.toolkit.fluxcd.io/v2.InstallRemediation">spec.install.remediation&lt;/a>,
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/components/helm/api/v2/#helm.toolkit.fluxcd.io/v2.UpgradeRemediation">spec.upgrade.remediation&lt;/a>,
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/components/helm/api/v2/#helm.toolkit.fluxcd.io/v2.Rollback">spec.rollback&lt;/a>
and
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/components/helm/api/v2/#helm.toolkit.fluxcd.io/v2.Uninstall">spec.uninstall&lt;/a>.
Features include the option to remediate with an uninstall after an upgrade failure,
and the option to keep a failed release for debugging purposes when it has run out of retries.&lt;/p>
&lt;p>Here is an example for configuring automated uninstalls (for all available fields,
consult the &lt;code>InstallRemediation&lt;/code> and &lt;code>Uninstall&lt;/code> API references linked above):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>helm.toolkit.fluxcd.io/v2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRelease&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>my-release&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>default&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># ...omitted for brevity&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">install&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Remediation configuration for when the Helm install&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># (or sequent Helm test) action fails&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">remediation&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Number of retries that should be attempted on failures before&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># bailing, a negative integer equals to unlimited retries&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">retries&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>-&lt;span style="color:#40a070">1&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Configuration options for the Helm uninstall action&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uninstall&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">timeout&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>5m&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">disableHooks&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">false&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">keepHistory&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">false&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here is an example of automated rollback configuration (for all available fields,
consult the &lt;code>UpgradeRemediation&lt;/code> and &lt;code>Rollback&lt;/code> API references linked above):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>helm.toolkit.fluxcd.io/v2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRelease&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>my-release&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>default&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># ...omitted for brevity&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">upgrade&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Remediaton configuration for when an Helm upgrade action fails&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">remediation&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Amount of retries to attempt after a failure,&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># setting this to 0 means no remedation will be&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># attempted&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">retries&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">5&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Configuration options for the Helm rollback action&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">rollback&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">timeout&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>5m&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">disableWait&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">false&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">disableHooks&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">false&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">recreate&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">false&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">force&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">false&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">cleanupOnFail&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">false&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="next-steps">Next Steps&lt;/h2>
&lt;ul>
&lt;li>
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/helmreleases/">Guides &amp;gt; Manage Helm Releases&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/components/helm/">Toolkit Components &amp;gt; Helm Controller&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/migration/helm-operator-migration/">Migration &amp;gt; Migrate to the Helm Controller&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Flux: Promote Flux Helm Releases with GitHub Actions</title><link>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/gh-actions-helm-promotion/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/gh-actions-helm-promotion/</guid><description>
&lt;p>This guide shows how to configure Flux and GitHub Actions to promote
Helm Releases across environments when a new Helm chart version is available.&lt;/p>
&lt;p>&lt;img src="https://deploy-preview-2413--fluxcd.netlify.app/img/flux-helm-github-promotion.png" alt="Flux Helm promotion GitHub workflow">&lt;/p>
&lt;p>For this guide we assume a scenario with two clusters: staging and production;
with the following promotion pipeline:&lt;/p>
&lt;ul>
&lt;li>On the staging cluster, Flux will monitor the Helm repository for new chart versions,
and it will automatically upgrade and test the Helm release.&lt;/li>
&lt;li>After the Helm release is successfully upgraded,
Flux will send an event to GitHub that will trigger a GitHub Actions workflow.&lt;/li>
&lt;li>The GitHub workflow receives the new chart version, updates the Flux &lt;code>HelmRelease&lt;/code>
manifest YAML for the production cluster and opens a Pull Request.&lt;/li>
&lt;li>When the Pull Request is merged, Flux upgrades the Helm release on the production
cluster to the chart version that was tested in staging.&lt;/li>
&lt;/ul>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>For this guide we assume you have two clusters bootstrapped with Flux and a good understanding
of how Flux manages Helm releases. Please see the
&lt;a href="https://github.com/fluxcd/flux2-kustomize-helm-example" target="_blank">helm example repository&lt;/a>
to familiarise yourself with Flux and Helm.&lt;/p>
&lt;h2 id="define-staging-and-production-releases">Define staging and production releases&lt;/h2>
&lt;p>For the staging cluster, we&amp;rsquo;ll define a &lt;code>HelmRelease&lt;/code> for which Flux will monitor the Helm repository,
and it will automatically upgrade the Helm release to the latest chart version based on a semver range.&lt;/p>
&lt;p>Example of &lt;code>clusters/staging/apps/demo.yaml&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>helm.toolkit.fluxcd.io/v2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRelease&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>demo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>apps&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">interval&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>60m&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">chart&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">chart&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>demo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">version&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;1.x&amp;#34;&lt;/span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># automatically upgrade to the latest version&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">interval&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>5m&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># scan the Helm repository every five minutes&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sourceRef&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRepository&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>demo-charts&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">test&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">enable&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># run tests on upgrades&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">valuesFrom&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Secret&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>demo-staging-values&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For the production cluster, we&amp;rsquo;ll define a &lt;code>HelmRelease&lt;/code> with a fixed version, the chart version will be
updated in Git by GitHub Actions based on the Flux events.&lt;/p>
&lt;p>Example of &lt;code>clusters/production/apps/demo.yaml&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>helm.toolkit.fluxcd.io/v2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRelease&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>demo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>apps&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">interval&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>60m&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">chart&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">chart&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>demo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># This field will be updated by GitHub Actions.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">version&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;1.0.0&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sourceRef&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRepository&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>demo-charts&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">valuesFrom&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Secret&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>demo-production-values&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="define-the-promotion-github-workflow">Define the promotion GitHub workflow&lt;/h2>
&lt;p>To promote a chart version that was successfully deployed and tested on staging, we&amp;rsquo;ll create a
GitHub workflow that reacts to Flux repository dispatch events.&lt;/p>
&lt;p>The &lt;code>event&lt;/code> that Flux generate for a &lt;code>HelmRelease&lt;/code> upgrade contains metadata about the event,
with fields that can be used to determine the chart version that was deployed:&lt;/p>
&lt;ul>
&lt;li>&lt;code>metadata.revision&lt;/code>: the Helm chart version deployed.&lt;/li>
&lt;li>&lt;code>metadata.oci-digest&lt;/code>(optional): the OCI digest of the oci artifact deployed in
case of an &lt;code>OCIRepository&lt;/code> source.&lt;/li>
&lt;/ul>
&lt;h3 id="promoting-a-helmrelease-with-a-chart-version">Promoting a HelmRelease with a chart version&lt;/h3>
&lt;p>The following &lt;code>.github/workflows/demo-promotion.yaml&lt;/code> workflow will react to the Flux
repository dispatch events and promote the Helm release to the production cluster.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>demo-promotion&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">on&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">repository_dispatch&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">types&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- HelmRelease/demo.apps&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">permissions&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">contents&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>write&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">pull-requests&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>write&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">jobs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">promote&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">runs-on&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ubuntu-latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Start promotion when the staging cluster has successfully&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># upgraded the Helm release to a new chart version.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">if&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> github.event.client_payload.metadata.env == &amp;#39;staging&amp;#39; &amp;amp;&amp;amp;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> github.event.client_payload.severity == &amp;#39;info&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">steps&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Checkout main branch.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>actions/checkout@v3&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">ref&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>main&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Parse the event metadata to determine the chart version deployed on staging.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Get chart version from staging&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>staging&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> VERSION=$(echo ${{ github.event.client_payload.metadata.revision }} | cut -d &amp;#39;@&amp;#39; -f1)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo VERSION=${VERSION} &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Patch the chart version in the production Helm release manifest.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Set chart version in production&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>production&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">CHART_VERSION&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ steps.staging.outputs.version }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo &amp;#34;set chart version to ${CHART_VERSION}&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> yq eval &amp;#39;.spec.chart.spec.version=env(CHART_VERSION)&amp;#39; -i ./clusters/production/apps/demo.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Open a Pull Request if an upgrade is needed in production.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Open promotion PR&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>peter-evans/create-pull-request@v4&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">branch&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>demo-promotion&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">delete-branch&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">token&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.GITHUB_TOKEN }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">commit-message&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Update demo to v${{ steps.staging.outputs.version }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">title&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Promote demo release to v${{ steps.staging.outputs.version }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">body&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> Promote demo release on production to v${{ steps.staging.outputs.version }}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> &lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The above workflow does the following:&lt;/p>
&lt;ul>
&lt;li>Runs on repository dispatch events issued by Flux with the &lt;code>HelmRelease/demo.apps&lt;/code> type.&lt;/li>
&lt;li>Filters the events to take into consideration only success Helm release upgrades.&lt;/li>
&lt;li>Clones the &lt;code>main&lt;/code> branch where the Flux &lt;code>HelmRelease&lt;/code> YAML manifests are defined.&lt;/li>
&lt;li>Parses the event metadata to determine the chart version deployed on staging.&lt;/li>
&lt;li>Patches the chart version in the &lt;code>HelmRelease&lt;/code> manifest at &lt;code>clusters/production/apps/demo.yaml&lt;/code>.&lt;/li>
&lt;li>Creates a new branch called &lt;code>demo-promotion&lt;/code>, commits the version change and opens a Pull Request against &lt;code>main&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Note&lt;/strong> that you should adapt the workflow to match your release name, namespace and YAML path.&lt;/p>
&lt;h3 id="promoting-a-helmrelease-using-oci-digests">Promoting a HelmRelease using OCI digests&lt;/h3>
&lt;p>The following &lt;code>.github/workflows/demo-promotion.yaml&lt;/code> workflow will react to the Flux
repository dispatch events and promote the Helm release to the production cluster.&lt;/p>
&lt;p>The expected &lt;code>HelmRelease&lt;/code> is one with a &lt;code>OCIRepository&lt;/code> source.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>demo-promotion&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">on&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">repository_dispatch&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">types&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- HelmRelease/demo.apps&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">permissions&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">contents&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>write&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">pull-requests&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>write&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">jobs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">promote&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">runs-on&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ubuntu-latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Start promotion when the staging cluster has successfully&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># upgraded the Helm release to a new chart version.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">if&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> github.event.client_payload.metadata.env == &amp;#39;staging&amp;#39; &amp;amp;&amp;amp;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> github.event.client_payload.severity == &amp;#39;info&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">steps&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Checkout main branch.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>actions/checkout@v3&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">ref&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>main&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Parse the event metadata to determine the chart version deployed on staging.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Get chart version from staging&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>staging&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> DIGEST=$(echo ${{ github.event.client_payload.metadata.oci-digest }} | cut -d &amp;#39;@&amp;#39; -f1)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo DIGEST=${DIGEST} &amp;gt;&amp;gt; $GITHUB_OUTPUT
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> VERSION=$(echo ${{ github.event.client_payload.metadata.revision }} | cut -d &amp;#39;@&amp;#39; -f1)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo VERSION=${VERSION} &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Patch the digest in the production OCIRepository manifest.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Set chart version in production&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>production&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">DIGEST&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ steps.staging.outputs.digest }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo &amp;#34;set ociRepository digest to ${DIGEST}&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> # This will filter out all resources that do not have a digest field.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> yq eval &amp;#39;(select(.spec.ref.digest) | .spec.ref.digest) = env(DIGEST)&amp;#39; -i ./clusters/production/apps/demo.yaml
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> # add the chart version as a line comment
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> env lc=&amp;#34;version ${{ steps.staging.outputs.version }}&amp;#34; \
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> yq eval &amp;#39;(select(.spec.ref.digest) | .spec.ref.digest) line_comment=env(lc)&amp;#39; -i ./clusters/production/apps/demo.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Open a Pull Request if an upgrade is needed in production.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Open promotion PR&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>peter-evans/create-pull-request@v4&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">branch&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>demo-promotion&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">delete-branch&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">token&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.GITHUB_TOKEN }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">commit-message&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Update demo to v${{ steps.staging.outputs.version }} with digest ${{ steps.staging.outputs.digest }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">title&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Promote demo release to v${{ steps.staging.outputs.version }} with digest ${{ steps.staging.outputs.digest }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">body&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> Promote demo release on production to v${{ steps.staging.outputs.version }} with digest ${{ steps.staging.outputs.digest }}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> &lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The above workflow does the following:&lt;/p>
&lt;ul>
&lt;li>Runs on repository dispatch events issued by Flux with the &lt;code>HelmRelease/demo.apps&lt;/code> type.&lt;/li>
&lt;li>Filters the events to take into consideration only success Helm release upgrades.&lt;/li>
&lt;li>Clones the &lt;code>main&lt;/code> branch where the Flux &lt;code>HelmRelease&lt;/code> YAML manifests are defined.&lt;/li>
&lt;li>Parses the event metadata to determine the chart version deployed on staging
and the corresponding OCI digest.&lt;/li>
&lt;li>Patches the digest in the &lt;code>OCIRepository&lt;/code> manifest at &lt;code>clusters/production/apps/demo.yaml&lt;/code>.&lt;/li>
&lt;li>Creates a new branch called &lt;code>demo-promotion&lt;/code>, commits the version change and opens a Pull Request against &lt;code>main&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Note&lt;/strong> that you should adapt the workflow to match your release name, namespace and YAML path.&lt;/p>
&lt;h2 id="configure-flux-for-repository-dispatching">Configure Flux for repository dispatching&lt;/h2>
&lt;p>On the staging cluster, we&amp;rsquo;ll configure Flux to send events to GitHub every time
it performs a Helm release upgrade.&lt;/p>
&lt;p>Example of &lt;code>clusters/staging/apps/demo-github.yaml&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>notification.toolkit.fluxcd.io/v1beta3&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Provider&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>github&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>apps&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">type&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>githubdispatch&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">address&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>https://github.com/org/repo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">secretRef&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>github-token&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#0e84b5;font-weight:bold">---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>notification.toolkit.fluxcd.io/v1beta3&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Alert&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>demo-dispatch&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>apps&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">providerRef&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>github&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">summary&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;Trigger promotion&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">eventMetadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>staging&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">cluster&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>staging-1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">region&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>eu-central-1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">eventSeverity&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>info&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">eventSources&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>HelmRelease&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>demo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">inclusionList&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#4070a0">&amp;#34;.*.upgrade.*succeeded.*&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>Note&lt;/strong> that you should adapt the above definitions to match your GitHub repository address.
If
&lt;a href="https://fluxcd.io/flux/components/helm/helmreleases/#test-configuration" target="_blank">testing is enabled&lt;/a>
in your HelmRelease, you can use the &lt;code>&amp;quot;.*.test.*succeeded.*&amp;quot;&lt;/code>
expression in the inclusion list instead of &lt;code>&amp;quot;.*.upgrade.*succeeded.*&amp;quot;&lt;/code>.
This will ensure the promotion happens only after tests have been successfully run.&lt;/p>
&lt;p>You also need to create a Kubernetes secret with a GitHub Personal Access Token
that has access to the repository:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>kubectl -n apps create secret generic github-token &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span>--from-literal&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bb60d5">token&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#70a0d0">${&lt;/span>&lt;span style="color:#bb60d5">GITHUB_TOKEN&lt;/span>&lt;span style="color:#70a0d0">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">GitHub PAT&lt;/h4>
Note that it is advised to create a dedicated user for Flux under your GitHub organisation.
Make the Flux user part of a GitHub team, so that you can give Flux access only
to the repositories used with &lt;code>flux bootstrap github&lt;/code>.
&lt;/div>
&lt;h2 id="relevant-documentation">Relevant documentation&lt;/h2>
&lt;ul>
&lt;li>
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/helmreleases/">Guides &amp;gt; Manage Helm Releases&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/components/source/helmrepositories/">Toolkit Components &amp;gt; Helm Repository API&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/components/helm/helmreleases/">Toolkit Components &amp;gt; Helm Release API&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/components/notification/providers/#github-dispatch">Toolkit Components &amp;gt; Notification API &amp;gt; GitHub Dispatch&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Flux: GitHub Actions Basic App Builder</title><link>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/gh-actions-app-builder/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/gh-actions-app-builder/</guid><description>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Disclaimer&lt;/h4>
&lt;p>This document is under review and may be declared out of scope for Flux.&lt;/p>
&lt;p>Note that this guide needs further review in consideration of Flux v2.0.0. It also predates the introduction of &lt;code>OCIRepository&lt;/code> and likely needs updates in consideration of those advancements.&lt;/p>
&lt;p>Expect this doc to either be archived soon, or to receive some overhaul.&lt;/p>
&lt;/div>
&lt;p>This guide shows how to configure GitHub Actions to build an image for each new commit pushed on a branch, for PRs, or for tags in the most basic way that Flux&amp;rsquo;s automation can work with and making some considerations for both dev and production.&lt;/p>
&lt;p>A single GitHub Actions workflow is presented with a few variations but one simple theme: Flux&amp;rsquo;s only firm requirement for integrating with CI is for the CI to build and push an image. So this document shows how to do just that.&lt;/p>
&lt;h3 id="scope-of-this-document">Scope of this document&lt;/h3>
&lt;p>Strictly speaking Flux considers CI to be out-of-scope, but this answer frequently leads to bad experiences caused by over-complicated CI built for users who did not firmly grasp the minimum requirements that Flux demands from a supporting CI. This example is intended to cover a majority of use cases with the simplest possible CI workflow.&lt;/p>
&lt;p>Users are not expected to strictly adopt this minimum viable solution or view this guidance as strongly prescriptive. You can adapt the example workflow for your use, and you can incorporate Flux&amp;rsquo;s automation into your dev or production release machinery at a variety of critical points, mostly independent of one another.&lt;/p>
&lt;p>We anticipate in this guide that Flux users who are developing one or more apps likely want two build strategies for each app: a &lt;strong>Dev&lt;/strong> build generates a (not semantically versioned) tag from some feature or environment branch with the branch name, commit hash, and timestamp; and a &lt;strong>Release&lt;/strong> build produces a
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/contributing/flux/#semantic-versioning">semantic version&lt;/a> tag from the release tag that preceded it.&lt;/p>
&lt;p>You might want deployment automation in either or both environments, or perhaps neither. This guide shows how to generate image tags in a way that will be ready to work with Flux&amp;rsquo;s automation for either or both of these scenarios.&lt;/p>
&lt;p>How to configure an &lt;code>ImageUpdateAutomation&lt;/code> resource to take advantage of Release or Dev builds with automation is covered separately in the
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/image-update/">Image Update Guide&lt;/a> and
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/sortable-image-tags/">Sortable image tags&lt;/a> guide, respectively.&lt;/p>
&lt;h2 id="example-github-actions-workflow">Example GitHub Actions Workflow&lt;/h2>
&lt;p>tl;dr: This build workflow does everything that Flux needs. Drop it into &lt;code>.github/workflows/docker-build.yml&lt;/code> and reap the benefits.&lt;/p>
&lt;p>First copy this example and update &lt;code>IMAGE&lt;/code> to point to your own image repository target. Then set &lt;code>DOCKERHUB_USERNAME&lt;/code> and &lt;code>DOCKERHUB_TOKEN&lt;/code> and you are done. Most git push events will now result in images suitable for Flux to deploy.&lt;/p>
&lt;p>For a deeper understanding and some variations, see the remainder of the doc.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Docker Build, Push&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">on&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">push&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">branches&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#4070a0">&amp;#39;*&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">tags-ignore&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#4070a0">&amp;#39;release/*&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">jobs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">docker&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">IMAGE&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kingdonb/any_old_app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">runs-on&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ubuntu-latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">steps&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Prepare&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>prep&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> BRANCH=${GITHUB_REF##*/}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> TS=$(date +%s)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> REVISION=${GITHUB_SHA::8}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> BUILD_ID=&amp;#34;${BRANCH}-${REVISION}-${TS}&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> LATEST_ID=canary
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> if [[ $GITHUB_REF == refs/tags/* ]]; then
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> BUILD_ID=${GITHUB_REF/refs\/tags\//}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> LATEST_ID=latest
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> fi
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo BUILD_DATE=$(date -u +&amp;#39;%Y-%m-%dT%H:%M:%SZ&amp;#39;) &amp;gt;&amp;gt; $GITHUB_OUTPUT
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo BUILD_ID=${BUILD_ID} &amp;gt;&amp;gt; $GITHUB_OUTPUT
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo LATEST_ID=${LATEST_ID} &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Set up QEMU&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>docker/setup-qemu-action@v3&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Set up Docker Buildx&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>docker/setup-buildx-action@v3&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Login to DockerHub&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>docker/login-action@v3&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">username&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.DOCKERHUB_USERNAME }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">password&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.DOCKERHUB_TOKEN }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Build and push&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>docker_build&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>docker/build-push-action@v6&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">push&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">tags&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> ${{ env.IMAGE }}:${{ steps.prep.outputs.BUILD_ID }}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> ${{ env.IMAGE }}:${{ steps.prep.outputs.LATEST_ID }}&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Image digest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>echo ${{ steps.docker_build.outputs.digest }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This workflow incorporates a few key concepts and properties which are important for Flux.&lt;/p>
&lt;h3 id="workflow-event-triggers">Workflow Event Triggers&lt;/h3>
&lt;p>There are two paths through this flow: when a commit is pushed to any branch and when a commit is pushed to any tag, with some exceptions possible as shown with &lt;code>tags-ignore:&lt;/code> – this example is given in case you are using the &lt;code>release/*&lt;/code> tags as shown in the
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/gh-actions-manifest-generation/#jsonnet-render-action">Jsonnet Render Action&lt;/a> example.&lt;/p>
&lt;p>These workflows are executed by GitHub Actions on the &lt;code>push&lt;/code> event for any branches and tags we specify.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">on&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">push&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">branches&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#4070a0">&amp;#39;*&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">tags-ignore&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#4070a0">&amp;#39;release/*&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You may want to invert or adjust these patterns depending on how you are using branches, image tags, and git tags. Flux is not prescriptive about any of this. Maybe you only build tags that match a certain pattern, or only commits on the &lt;code>main&lt;/code> branch, depending on the need. Some variations are expected and they are out of scope for this guide.&lt;/p>
&lt;p>Now let&amp;rsquo;s walk through the rest of this example workflow.&lt;/p>
&lt;h3 id="docker-build-job">Docker Build job&lt;/h3>
&lt;p>The workflow has one job with the id &lt;code>docker&lt;/code> whose purpose is to turn commits from push events into deployable images.&lt;/p>
&lt;p>An individual image tag name (string) has two parts, &lt;code>IMAGE&lt;/code> which represents the image name that is common for all images in the same project, and following that image name separated by a colon is a &lt;code>tag&lt;/code> which uniquely identifies a revision of the image. Repositories can hold many tags, and tags can utilize various forms and formats.&lt;/p>
&lt;h4 id="mutable-vs-immutable-tags">Mutable vs. Immutable tags&lt;/h4>
&lt;p>Image tags can be mutable or immutable. Flux works best with immutable tags: &lt;code>latest&lt;/code> and &lt;code>canary&lt;/code> are examples of mutable tags.&lt;/p>
&lt;p>This example produces both mutable and immutable tags because Flux works with immutable tags, but many users still expect a &lt;code>latest&lt;/code> tag even if Flux won&amp;rsquo;t be able to take advantage of it. Mutable tags are useful for example with environment branches, to stably represent the latest build in a named environment, but their use is generally contrary to GitOps principles. Flux automation demands immutable tags, with a timestamp or something else sortable in the tag string. Thus mutable tags alone are not suitable for most purposes in Flux.&lt;/p>
&lt;p>In this example, &lt;code>LATEST_ID&lt;/code> represents a mutable tag and &lt;code>latest&lt;/code> as a tag represents the last release build that was pushed from any Git tag. The &lt;code>canary&lt;/code> tag is the last image that was pushed from any branch.&lt;/p>
&lt;p>&lt;code>BUILD_ID&lt;/code> represents the immutable tag in both the dev and release path. This is either a literal tag string from Git tag (Flux works best with semver tags) or a &lt;code>${BRANCH}-${REVISION}-${TS}&lt;/code> in this build workflow.&lt;/p>
&lt;p>The mutable tags &lt;code>canary&lt;/code> and &lt;code>latest&lt;/code> are chosen by the script depending on which event triggered the build. If the image is built from a tag, the &lt;code>latest&lt;/code> tag is used. If it is built from a branch, &lt;code>canary&lt;/code> is used instead. These tags will therefore always point at the &amp;ldquo;latest&amp;rdquo; release tag and the latest &amp;ldquo;canary&amp;rdquo; however you define it.&lt;/p>
&lt;p>This example shows one useful convention among many possible uses for mutable image tags.&lt;/p>
&lt;p>Another sensible choice could be to build and push canary images only from the &lt;code>main&lt;/code> branch. This script can be as elaborate as you want, the important logic is all contained in the shell script embedded in the &lt;code>Prepare&lt;/code> step:&lt;/p>
&lt;h3 id="prepare-step">Prepare Step&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">steps&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Prepare&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>prep&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> BRANCH=${GITHUB_REF##*/}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> TS=$(date +%s)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> REVISION=${GITHUB_SHA::8}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> BUILD_ID=&amp;#34;${BRANCH}-${REVISION}-${TS}&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> LATEST_ID=canary
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> if [[ $GITHUB_REF == refs/tags/* ]]; then
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> BUILD_ID=${GITHUB_REF/refs\/tags\//}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> LATEST_ID=latest
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> fi
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo BUILD_DATE=$(date -u +&amp;#39;%Y-%m-%dT%H:%M:%SZ&amp;#39;) &amp;gt;&amp;gt; $GITHUB_OUTPUT
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo BUILD_ID=${BUILD_ID} &amp;gt;&amp;gt; $GITHUB_OUTPUT
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo LATEST_ID=${LATEST_ID} &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This script has no external effects, it only takes some inputs from environment variables set by GitHub Actions and calculates them into several outputs: &lt;code>BUILD_ID&lt;/code> and &lt;code>LATEST_ID&lt;/code>. The &lt;code>BUILD_DATE&lt;/code> is also exported as an output for informational purposes and is not used elsewhere in the workflow.&lt;/p>
&lt;p>&lt;code>TS&lt;/code> is the Unix timestamp in seconds, a monotonically increasing value that represents when the build got scheduled. This lets us reliably determine what build is actually latest, even when some builds may take longer or shorter.&lt;/p>
&lt;div class="alert alert-primary" role="alert">
&lt;h4 class="alert-heading">Use Immutable Tags&lt;/h4>
This section highlights another advantage of Flux&amp;rsquo;s requirement for using timestamped tags instead of a mutable &lt;code>latest&lt;/code> tag, in which case the longest build (and not necessarily the latest promoted build) can occasionally win out.
&lt;/div>
&lt;p>&lt;code>REVISION&lt;/code> is the first 8 characters of the &lt;code>GITHUB_SHA&lt;/code>, a fingerprint that is kept for humans to differentiate more easily between tags strings that are very similar. It is not meaningful for Flux and can be omitted if preferred. Only &lt;code>TIMESTAMP&lt;/code> has any function as it is needed to create an &lt;code>ImagePolicy&lt;/code> (reference:
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/sortable-image-tags/">Sortable image tags&lt;/a>).&lt;/p>
&lt;h3 id="dependencies-setup">Dependencies Setup&lt;/h3>
&lt;p>These steps prepare the build environment with QEMU and Docker:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Set up QEMU&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># ...&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Set up Docker Buildx&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="dockerhub-login">DockerHub Login&lt;/h3>
&lt;p>Secrets for your container registry with read and write access can be added in GitHub as
&lt;a href="https://docs.github.com/en/actions/security-guides/encrypted-secrets" target="_blank">Encrypted secrets&lt;/a> and retrieved for use when pushing images.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Login to DockerHub&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># ...&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">username&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.DOCKERHUB_USERNAME }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">password&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.DOCKERHUB_TOKEN }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If the GitHub Container Registry (
&lt;a href="https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry" target="_blank">GHCR.io&lt;/a>) is used, users can skip encrypting secrets and use the &lt;code>write:packages&lt;/code> scope with ambient &lt;code>GITHUB_TOKEN&lt;/code> instead. The
&lt;a href="https://github.com/docker/login-action#github-container-registry" target="_blank">Docker Login action&lt;/a> has more specific instructions.&lt;/p>
&lt;h3 id="build-and-push-tags">Build and push tag(s)&lt;/h3>
&lt;p>Now that Docker is logged in, a generic build and push is invoked, pushing both a mutable and an immutable image tag:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Build and push&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>docker_build&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># ...&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">push&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">tags&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> ${{ env.IMAGE }}:${{ steps.prep.outputs.BUILD_ID }}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> ${{ env.IMAGE }}:${{ steps.prep.outputs.LATEST_ID }}&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>An image digest is printed at the end for information.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Image digest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>echo ${{ steps.docker_build.outputs.digest }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="further-reading-on-ci">Further Reading on CI&lt;/h3>
&lt;p>In
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/image-update/">Image Update Guide&lt;/a> we can see how Flux&amp;rsquo;s image update automation works with these image tags. In the
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/gh-actions-manifest-generation/">GitHub Actions Manifest Generation&lt;/a> guide, we see more CI workflows that go even further, rendering manifests in CI and committing them back to Git.&lt;/p>
&lt;p>Some of those techniques are quite advanced beyond what is actually needed to work with Flux, but those suggested readings may clarify some of what other possibilities there are for Flux to work with different automation.&lt;/p>
&lt;p>In general these approaches all embrace Git as a single source of truth, either pushing their updated truth as a new input for a standard GitOps deployment, or using another Git target as an intermediate store that still derives from GitOps intents declared further upstream. So automation can push directly back to a default branch, or we can configure Flux to
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/image-update/#push-updates-to-a-different-branch">Push updates to a different branch&lt;/a> than the one used for checkout.&lt;/p>
&lt;p>When Flux pushes directly to a default branch, those changes are deployed automatically on the next reconcile, or immediately with
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/webhook-receivers/">Webhook Receivers&lt;/a>. When pushing to a different branch the
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/gh-actions-auto-pr/">GitHub Actions Auto Pull Request&lt;/a> workflow is another option that can be used to keep some automation with manual control. The developers or other project stakeholders can then merge a PR that automation generated in order to manually promote the change in an automated way.&lt;/p>
&lt;p>Further expansion on a more intricate design for CI that does any more than what Flux demands is out of scope. Some useful ideas for further enhancement in the scope of CI beyond this scope boundary are suggested nonetheless below.&lt;/p>
&lt;h4 id="image-provenance-security">Image Provenance Security&lt;/h4>
&lt;p>This guide does not cover or implement Image Provenance or any cryptographic signature, but Flux does provide examples of those workflows as they are implemented in Flux&amp;rsquo;s own controllers!&lt;/p>
&lt;p>Another exercise for the reader to implement after this basic builder could be implementing Cosign for cryptographically proving the image provenance as described in
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/blog/2022/02/security-image-provenance/">Security: Image Provenance&lt;/a>.&lt;/p>
&lt;h4 id="caching-for-fast-builds">Caching for Fast Builds&lt;/h4>
&lt;p>One last bit of general parting guidance: Flux&amp;rsquo;s deploy automation is designed to be scalable and fast. To make the developer experience good requires fast CI builds as well. Slow CI builds detract sharply from the experience; the faster the better as more time waiting for feedback from a build adds to cognitive load and context switching. Greater time spent waiting for CI/CD can unfortunately have outsized impacts on focus depletion and developer productivity.&lt;/p>
&lt;p>The build result may provide test-driven feedback to support fast iteration for high-functioning rapid delivery teams. An average time of longer than 5 minutes to get that feedback may already be too long. If your CI builds for iterative development are taking much longer than 5 minutes, it&amp;rsquo;s a good idea to start to consider some approaches to make them faster.&lt;/p>
&lt;p>A skillfully designed &lt;code>Dockerfile&lt;/code> can help provide some relief for builds that are too slow with heavyweight prerequisites that necessarily take a long time to build. Arranging your build order so the slow parts that change less frequently are built first, or in a separate staging, means they can be cached and repeated only as often as they change.&lt;/p>
&lt;p>This is one good fundamental approach to reduce build times. On the topic of caching, more information that goes with this example is provided in the
&lt;a href="https://github.com/docker/build-push-action/blob/master/docs/advanced/cache.md" target="_blank">docker/build-push-action Cache&lt;/a> documentation.&lt;/p></description></item><item><title>Flux: GitHub Actions Manifest Generation</title><link>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/gh-actions-manifest-generation/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/gh-actions-manifest-generation/</guid><description>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Disclaimer&lt;/h4>
&lt;p>This document is under review and may be declared out of scope for Flux.&lt;/p>
&lt;p>Note that this guide no longer describes the recommended way to handle manifest generation in Flux. It also predates the introduction of &lt;code>OCIRepository&lt;/code> and needs updates in consideration of those advancements.&lt;/p>
&lt;p>Expect this doc to either be archived soon, or to receive a major overhaul in support of the new preferred approach described below.&lt;/p>
&lt;/div>
&lt;div class="alert alert-info" role="alert">
&lt;h4 class="alert-heading">Author's Note&lt;/h4>
&lt;p>If you want to use Flux with tooling-generated manifests today, you should capture the output and store it in an OCI Artifact with &lt;code>flux push artifact&lt;/code>. The following doc uses an old method that has many disadvantages: it can&amp;rsquo;t be provenance secured with Cosign. The artifacts can&amp;rsquo;t be indexed as efficiently as an OCI registry&amp;rsquo;s tag-based distribution. Nor can the delivery of a private Git repository be authenticated and authorized in a workload cluster, using any cloud-based IAM or ambient environmental credentials that are typically used to secure a private OCI registry.&lt;/p>
&lt;p>These methods were developed to bridge the gap for Flux users transitioning from Flux v1. The introduction of the &lt;code>OCIRepository&lt;/code> since then, has nigh obsoleted the approach shown here. Please follow the OCI Cheatsheet guide to understand what is possible, and migrate your workflows to use Flux OCI!&lt;/p>
&lt;/div>
&lt;p>This example implements &amp;ldquo;build-time&amp;rdquo; manifest generation on GitHub Actions.&lt;/p>
&lt;p>Third-party tools are used to generate YAML manifests in a CI job. The updated YAML are committed and pushed to Git, where &lt;code>kustomize-controller&lt;/code> finally applies them.&lt;/p>
&lt;h3 id="background">Background&lt;/h3>
&lt;p>There are many use cases for manifest generation tools, but Flux v2 no longer permits embedding arbitrary binaries with the Flux machinery to run at apply time.&lt;/p>
&lt;p>Flux (kustomize-controller) will apply whatever revision of the manifests are at the latest commit, on any branch it is pointed at. By design, Flux doesn&amp;rsquo;t care for any details of how a commit is generated.&lt;/p>
&lt;p>Since
&lt;a href="https://github.com/fluxcd/flux2/discussions/802" target="_blank">&amp;ldquo;select latest by build time&amp;rdquo; image automation&lt;/a> is deprecated, and since
&lt;a href="https://github.com/fluxcd/flux2/issues/543" target="_blank">&lt;code>.flux.yaml&lt;/code> is also deprecated&lt;/a>, some staple workflows are no longer possible without new accommodations from infrastructure.&lt;/p>
&lt;h4 id="what-should-we-do">What Should We Do?&lt;/h4>
&lt;p>We first recommend users
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/guides/sortable-image-tags/">adjust their tagging strategies&lt;/a>, which is made clear elsewhere in the docs. This is usually a straightforward adjustment, and enables the use of
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/image-update/">Image Update Policies&lt;/a>; however this may not be feasible or desired in some cases.&lt;/p>
&lt;h2 id="use-manifest-generation">Use Manifest Generation&lt;/h2>
&lt;p>Introducing, Manifest Generation with Jsonnet, for
&lt;a href="https://github.com/kingdonb/any_old_app" target="_blank">any old app&lt;/a> on GitHub!&lt;/p>
&lt;p>If you have followed the
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/get-started/">Flux bootstrap guide&lt;/a> and only have one &lt;code>fleet-infra&lt;/code> repository, it is recommended to create a separate repository that represents your application for this use case guide, or clone the repository linked above in order to review these code examples which have already been implemented there.&lt;/p>
&lt;h3 id="primary-uses-of-flux">Primary Uses of Flux&lt;/h3>
&lt;p>Flux&amp;rsquo;s primary use case for &lt;code>kustomize-controller&lt;/code> is to apply YAML manifests from the latest &lt;code>Revision&lt;/code> of an &lt;code>Artifact&lt;/code>.&lt;/p>
&lt;h3 id="security-consideration">Security Consideration&lt;/h3>
&lt;p>Flux v2 can not be configured to call out to arbitrary binaries that a user might supply with an &lt;code>InitContainer&lt;/code>, as it was possible to do in Flux v1.&lt;/p>
&lt;h4 id="motivation-for-this-guide">Motivation for this Guide&lt;/h4>
&lt;p>In Flux v2 it is assumed if users want to run more than &lt;code>Kustomize&lt;/code> with &lt;code>envsubst&lt;/code>, that it will be done outside of Flux; the goal of this guide is to show several common use cases of this pattern in secure ways.&lt;/p>
&lt;h4 id="demonstrated-concepts">Demonstrated Concepts&lt;/h4>
&lt;p>It is intended, finally, to show through this use case, three fundamental ideas for use in CI to accompany Flux automation:&lt;/p>
&lt;ol>
&lt;li>Writing workflow that can commit changes back to the same branch of a working repository.&lt;/li>
&lt;li>A workflow to commit generated content from one directory into a different branch in the repository.&lt;/li>
&lt;li>Workflow to commit from any source directory into a target branch on a different repository.&lt;/li>
&lt;/ol>
&lt;p>Readers can interpret this document with adaptations for use with other CI providers, or Git source hosts, or manifest generators.&lt;/p>
&lt;p>Jsonnet is demonstrated with examples presented in sufficient depth that, hopefully, Flux users who are not already familiar with manifest generation or Jsonnet can pick up &lt;code>kubecfg&lt;/code> and start using it to solve novel and interesting configuration problems.&lt;/p>
&lt;h3 id="the-choice-of-github-actions">The Choice of GitHub Actions&lt;/h3>
&lt;p>There are authentication concerns to address with every CI provider and they also differ by Git provider.&lt;/p>
&lt;p>Given that GitHub Actions are hosted on GitHub, this guide can be streamlined in some ways. We can almost completely skip configuring authentication. The cross-cutting concern is handled by the CI platform, except in our fourth and final example, the &lt;em>Commit Across Repositories Workflow&lt;/em>.&lt;/p>
&lt;p>From a GitHub Action, as we must have been authenticated to write to a branch, Workflows also can transitively gain write access to the repo safely.&lt;/p>
&lt;p>Mixing and matching from other providers like Bitbucket Cloud, Jenkins, or GitLab will need more attention to these details for auth configurations. GitHub Actions is a platform that is designed to be secure by default.&lt;/p>
&lt;h2 id="manifest-generation-examples">Manifest Generation Examples&lt;/h2>
&lt;p>There are several use cases presented.&lt;/p>
&lt;ul>
&lt;li>
&lt;a href="#string-substitution-with-sed--i">String Substitution with sed -i&lt;/a>&lt;/li>
&lt;li>
&lt;a href="#docker-build-and-tag-with-version">Docker Build and Tag with Version&lt;/a>&lt;/li>
&lt;li>
&lt;a href="#jsonnet-for-yaml-document-rehydration">Jsonnet for YAML Document Rehydration&lt;/a>&lt;/li>
&lt;li>
&lt;a href="#commit-across-repositories-workflow">Commit Across Repositories Workflow&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>In case these examples are too heavy, this short link guide can help you navigate the four main examples. Finally, the code examples we&amp;rsquo;ve all been waiting for, the answer to complex &lt;code>.flux.yaml&lt;/code> configs in Flux v2! 🎉🎁&lt;/p>
&lt;h3 id="string-substitution-with-sed--i">String Substitution with &lt;code>sed -i&lt;/code>&lt;/h3>
&lt;p>The entry point for these examples begins at &lt;code>.github/workflows/&lt;/code> in any GitHub source repository where your YAML manifests are stored.&lt;/p>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">GitRepository source only targets one branch&lt;/h4>
While this first example operates on any branch (&lt;code>branches: ['*']&lt;/code>), each &lt;code>Kustomization&lt;/code> in Flux only deploys manifests from &lt;strong>one branch or tag&lt;/strong> at a time. Understanding this is key for managing large Flux deployments and clusters with multiple &lt;code>Kustomizations&lt;/code> and/or crossing several environments.
&lt;/div>
&lt;p>First add this directory if needed in your repositories. Find the example below in context, and read on to understand how it works:
&lt;a href="https://github.com/kingdonb/any_old_app/blob/main/.github/workflows/01-manifest-generate.yaml" target="_blank">01-manifest-generate.yaml&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># ./.github/workflows/01-manifest-generate.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Manifest Generation&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">on&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">push&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">branches&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#4070a0">&amp;#39;*&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">jobs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Push Git Update&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">runs-on&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ubuntu-latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">steps&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Prepare&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>prep&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> VERSION=${GITHUB_SHA::8}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo BUILD_DATE=$(date -u +&amp;#39;%Y-%m-%dT%H:%M:%SZ&amp;#39;) &amp;gt;&amp;gt; $GITHUB_OUTPUT
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo VERSION=${VERSION} &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Checkout repo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>actions/checkout@v3&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Update manifests&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>./update-k8s.sh $GITHUB_SHA&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Commit changes&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>EndBug/add-and-commit@v7&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">add&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#39;.&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">message&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;[ci skip] deploy from ${{ steps.prep.outputs.VERSION }}&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">signoff&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In the &lt;code>Prepare&lt;/code> step, even before the clone, GitHub Actions provides metadata about the commit. Then, &lt;code>Checkout repo&lt;/code> performs a shallow clone for the build.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># excerpt from above - set two outputs named &amp;#34;VERSION&amp;#34; and &amp;#34;BUILD_DATE&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">VERSION&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#70a0d0">${&lt;/span>&lt;span style="color:#bb60d5">GITHUB_SHA&lt;/span>::&lt;span style="color:#bb60d5">8&lt;/span>&lt;span style="color:#70a0d0">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">BUILD_DATE&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#007020;font-weight:bold">$(&lt;/span>date -u +&lt;span style="color:#4070a0">&amp;#39;%Y-%m-%dT%H:%M:%SZ&amp;#39;&lt;/span>&lt;span style="color:#007020;font-weight:bold">)&lt;/span> &amp;gt;&amp;gt; &lt;span style="color:#bb60d5">$GITHUB_OUTPUT&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">echo&lt;/span> &lt;span style="color:#bb60d5">VERSION&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#70a0d0">${&lt;/span>&lt;span style="color:#bb60d5">VERSION&lt;/span>&lt;span style="color:#70a0d0">}&lt;/span> &amp;gt;&amp;gt; &lt;span style="color:#bb60d5">$GITHUB_OUTPUT&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;div class="alert alert-primary" role="alert">
&lt;h4 class="alert-heading">When migrating to Flux v2&lt;/h4>
Users will find that
&lt;a href="https://github.com/fluxcd/flux2/discussions/802#discussioncomment-320189" target="_blank">some guidance has changed since Flux v1&lt;/a>. Tagging images with a &lt;code>GIT_SHA&lt;/code> was a common practice that is no longer supported by Flux&amp;rsquo;s Image Automation. A newer alternative is adding timestamp or build number in
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/sortable-image-tags/">Sortable image tags&lt;/a>, preferred by the &lt;code>image-automation-controller&lt;/code>.
&lt;/div>
&lt;p>Next we call out to a shell script &lt;code>update-k8s.sh&lt;/code> taking one argument, the Git SHA value from GitHub:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># excerpted from above - run a shell script&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Update manifests&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>./update-k8s.sh $GITHUB_SHA&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>That script is below. It performs two in-place string substitutions using &lt;code>sed&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># update-k8s.sh&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">set&lt;/span> -feu &lt;span style="color:#60a0b0;font-style:italic"># Usage: $0 &amp;lt;GIT_SHA&amp;gt; # Fails when GIT_SHA is not provided&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">GIT_SHA&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#70a0d0">${&lt;/span>&lt;span style="color:#bb60d5">1&lt;/span>:&lt;span style="color:#bb60d5">0&lt;/span>:&lt;span style="color:#bb60d5">8&lt;/span>&lt;span style="color:#70a0d0">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sed -i &lt;span style="color:#4070a0">&amp;#34;s|image: kingdonb/any-old-app:.*|image: kingdonb/any-old-app:&lt;/span>&lt;span style="color:#bb60d5">$GIT_SHA&lt;/span>&lt;span style="color:#4070a0">|&amp;#34;&lt;/span> k8s.yml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sed -i &lt;span style="color:#4070a0">&amp;#34;s|GIT_SHA: .*|GIT_SHA: &lt;/span>&lt;span style="color:#bb60d5">$GIT_SHA&lt;/span>&lt;span style="color:#4070a0">|&amp;#34;&lt;/span> flux-config/configmap.yaml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>update-k8s.sh&lt;/code> receives &lt;code>GITHUB_SHA&lt;/code> that the script trims down to 8 characters.&lt;/p>
&lt;p>Then, &lt;code>sed -i&lt;/code> runs twice, updating &lt;code>k8s.yml&lt;/code> and &lt;code>flux-config/configmap.yaml&lt;/code> which are also provided as examples here. The new SHA value is added twice, once in each file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># k8s.yml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>apps/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Deployment&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>any-old-app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">replicas&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">1&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">selector&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">matchLabels&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">app&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>any-old-app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">template&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">labels&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">app&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>any-old-app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">containers&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">image&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kingdonb/any-old-app:4f314627&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>any-old-app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#0e84b5;font-weight:bold">---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Service&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>any-old-app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">type&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ClusterIP&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">ports&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;any-old-app&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">port&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">3000&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">selector&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">app&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>any-old-app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The convention of including a &lt;code>k8s.yml&lt;/code> file in one&amp;rsquo;s application repository is borrowed from
&lt;a href="https://github.com/okteto/go-getting-started/blob/master/k8s.yml" target="_blank">Okteto&amp;rsquo;s Getting Started Guides&lt;/a>, as a simplified example.&lt;/p>
&lt;p>The &lt;code>k8s.yml&lt;/code> file in the application root is not meant to be applied by Flux, but might be a handy template to keep fresh as a developer reference nonetheless.&lt;/p>
&lt;p>The file below, &lt;code>configmap.yaml&lt;/code>, is placed in a directory &lt;code>flux-config/&lt;/code> which will be synchronized to the cluster by a &lt;code>Kustomization&lt;/code> that we will add in the following step.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># flux-config/configmap.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">data&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">GIT_SHA&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>4f314627&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ConfigMap&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">creationTimestamp&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">null&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>any-old-app-version&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>devl&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>These are the two files that are re-written in the &lt;code>sed -i&lt;/code> example above.&lt;/p>
&lt;p>A configmap is an ideal place to write a variable that is needed by any downstream &lt;code>Kustomization&lt;/code>, for example to use with &lt;code>envsubst&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#0e84b5;font-weight:bold">---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kustomize.toolkit.fluxcd.io/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Kustomization&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>any-old-app-devl&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">interval&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>15m0s&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">path&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>./&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">prune&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sourceRef&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>GitRepository&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>any-old-app-prod&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">targetNamespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>prod&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">postBuild&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">substituteFrom&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ConfigMap&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>any-old-app-version&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#0e84b5;font-weight:bold">---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>source.toolkit.fluxcd.io/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>GitRepository&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>any-old-app-prod&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>prod&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">interval&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>20m0s&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">ref&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">branch&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>deploy&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">secretRef&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-secret&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">url&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ssh://git@github.com/kingdonb/csh-flux&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, any downstream &lt;code>Deployment&lt;/code> in the &lt;code>Kustomization&lt;/code> can write a &lt;code>PodSpec&lt;/code> like this one, to reference the image from the latest commit referenced by the &lt;code>ConfigMap&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># flux/ some-example-deployment.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">replicas&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">1&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">selector&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">matchLabels&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">app&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>any-old-app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">template&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">labels&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">app&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>any-old-app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">containers&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">image&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kingdonb/any-old-app:${GIT_SHA}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>any-old-app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Deployment specifications will vary, so adapting this example is left as exercise for the reader. Write it together with a kustomization.yaml, or just add this to a subdirectory anywhere within your Flux Kustomization path.&lt;/p>
&lt;h3 id="docker-build-and-tag-with-version">Docker Build and Tag with Version&lt;/h3>
&lt;p>Now for another staple workflow: building and pushing an OCI image tag from a Dockerfile in any branch or tag.&lt;/p>
&lt;p>From the Actions marketplace,
&lt;a href="https://github.com/marketplace/actions/build-and-push-docker-images" target="_blank">Build and push Docker images&lt;/a> provides the heavy lifting in this example. Flux has nothing to do with building images, but we include this still — as some images will need to be built for our use in these examples.&lt;/p>
&lt;div class="alert alert-primary" role="alert">
&lt;h4 class="alert-heading">ImageRepository can reflect both branches and tags&lt;/h4>
This example builds an image for any branch or tag ref and pushes it to Docker Hub. (Note the omission of &lt;code>branches: ['*']&lt;/code> that was in the prior example.) GitHub Secrets &lt;code>DOCKERHUB_USERNAME&lt;/code> and &lt;code>DOCKERHUB_TOKEN&lt;/code> are used here to authenticate with Docker Hub from within GitHub Actions.
&lt;/div>
&lt;p>We again borrow a
&lt;a href="https://github.com/fluxcd/kustomize-controller/blob/5da1fc043db4a1dc9fd3cf824adc8841b56c2fcd/.github/workflows/release.yml#L17-L25" target="_blank">Prepare step&lt;/a> from Kustomize Controller&amp;rsquo;s own release workflow. Find the example below in context,
&lt;a href="https://github.com/kingdonb/any_old_app/blob/main/.github/workflows/02-docker-build.yaml" target="_blank">02-docker-build.yaml&lt;/a>, or copy it from below.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># ./.github/workflows/02-docker-build.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Docker Build, Push&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">on&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">push&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">branches&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#4070a0">&amp;#39;*&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">tags-ignore&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#4070a0">&amp;#39;release/*&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">jobs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">docker&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">runs-on&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ubuntu-latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">steps&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Prepare&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>prep&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> VERSION=${GITHUB_SHA::8}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> if [[ $GITHUB_REF == refs/tags/* ]]; then
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> VERSION=${GITHUB_REF/refs\/tags\//}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> fi
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo BUILD_DATE=$(date -u +&amp;#39;%Y-%m-%dT%H:%M:%SZ&amp;#39;) &amp;gt;&amp;gt; $GITHUB_OUTPUT
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo VERSION=${VERSION} &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Set up QEMU&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>docker/setup-qemu-action@v2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Set up Docker Buildx&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>docker/setup-buildx-action@v2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Login to DockerHub&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>docker/login-action@v2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">username&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.DOCKERHUB_USERNAME }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">password&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.DOCKERHUB_TOKEN }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Build and push&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>docker_build&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>docker/build-push-action@v4&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">push&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">tags&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kingdonb/any-old-app:${{ steps.prep.outputs.VERSION }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Image digest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>echo ${{ steps.docker_build.outputs.digest }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The
&lt;a href="https://github.com/marketplace/actions/docker-login" target="_blank">Docker Login Action&lt;/a> is used here to enable an authenticated image push.&lt;/p>
&lt;p>Any secrets from GitHub Secrets can be used as shown, and support for image registries is explained in the linked README. Add a setting for &lt;code>registry&lt;/code> if your app uses any private registry, rather than the implicit Docker Hub registry above.&lt;/p>
&lt;pre tabindex="0">&lt;code># for example
with:
registry: registry.cloud.okteto.net
&lt;/code>&lt;/pre>&lt;p>The image tag &lt;code>VERSION&lt;/code> comes from the branch or Git tag that triggered the build. Whether that version is a &lt;code>GIT_SHA&lt;/code> or a Semantic Version, (or anything in between!) the same workflow can be used to build an OCI image as shown here.&lt;/p>
&lt;h3 id="jsonnet-for-yaml-document-rehydration">Jsonnet for YAML Document Rehydration&lt;/h3>
&lt;p>As mentioned before, Flux only monitors one branch or tag per Kustomization.&lt;/p>
&lt;p>In the earlier examples, no fixed branch target was specified. Whatever branch triggered the workflow, received the generated YAMLs in the next commit.&lt;/p>
&lt;p>If you created your deployment manifests in any branch, the &lt;code>deploy&lt;/code> branch or otherwise, it is necessary to add another &lt;code>Kustomization&lt;/code> and &lt;code>GitRepository&lt;/code> source to apply manifests from that branch and path in the cluster.&lt;/p>
&lt;p>In application repositories, it is common to maintain an environment branch, a release branch, or both. Some additional Flux objects may be needed for each new environment target with its own branch. Jsonnet can be used for more easily managing heavyweight repetitive boilerplate configuration such as this.&lt;/p>
&lt;p>It is recommended to follow these examples as they are written for better understanding, then later change and adapt them for your own release practices and environments.&lt;/p>
&lt;div class="alert alert-primary" role="alert">
&lt;h4 class="alert-heading">GitRepository source only targets one branch&lt;/h4>
Since Flux uses one branch per Kustomization, to trigger an update we must write to a &lt;code>deploy&lt;/code> branch or tag. Even when new app images can come from any branch (eg. for Dev environments where any latest commit is to be deployed) the YAML manifests to deploy will be sourced from just one branch.
&lt;/div>
&lt;p>It is advisable to protect repository main and release branches with eg. branch policies and review requirements, as through automation, these branches can directly represent the production environment.&lt;/p>
&lt;p>The CI user for this example should be allowed to push directly to the &lt;code>deploy&lt;/code> branch that Kustomize deploys from; this branch also represents the environment so must be protected in a similar fashion to &lt;code>release&lt;/code>.&lt;/p>
&lt;p>Only authorized people (and build robots) should be allowed to make writes to a &lt;code>deploy&lt;/code> branch.&lt;/p>
&lt;h4 id="jsonnet-render-action">Jsonnet Render Action&lt;/h4>
&lt;p>In this example, the outputted YAML manifests, (on successful completion of the Jsonnet render step,) are staged on the &lt;code>deploy&lt;/code> branch, then committed and pushed.&lt;/p>
&lt;p>The latest commit on the &lt;code>deploy&lt;/code> branch is reconciled into the cluster by another &lt;code>Kustomization&lt;/code> that is omitted here, as it is assumed that users who have read this far already added this in the previous examples.&lt;/p>
&lt;p>You may find the example below in context,
&lt;a href="https://github.com/kingdonb/any_old_app/blob/main/.github/workflows/03-release-manifests.yaml" target="_blank">03-release-manifests.yaml&lt;/a>, or simply copy it from below.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># ./.github/workflows/03-release-manifests.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Build jsonnet&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">on&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">push&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">tags&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>[&lt;span style="color:#4070a0">&amp;#39;release/*&amp;#39;&lt;/span>]&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">branches&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>[&lt;span style="color:#4070a0">&amp;#39;release&amp;#39;&lt;/span>]&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">jobs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>jsonnet push&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">runs-on&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ubuntu-latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">steps&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Prepare&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>prep&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> VERSION=${GITHUB_SHA::8}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> if [[ $GITHUB_REF == refs/tags/release/* ]]; then
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> VERSION=${GITHUB_REF/refs\/tags\/release\//}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> fi
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo BUILD_DATE=$(date -u +&amp;#39;%Y-%m-%dT%H:%M:%SZ&amp;#39;) &amp;gt;&amp;gt; $GITHUB_OUTPUT
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo VERSION=${VERSION} &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Checkout repo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>actions/checkout@v3&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Setup kubecfg CLI&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kingdonb/kubecfg/action@main&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kubecfg show&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kubecfg show manifests/example.jsonnet &amp;gt; output/production.yaml&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Prepare target branch&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>./ci/rake.sh deploy&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Commit changes&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>EndBug/add-and-commit@v7&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">add&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#39;production.yaml&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">branch&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>deploy&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">message&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;[ci skip] from ${{ steps.prep.outputs.VERSION }}&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">signoff&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We add three new steps in this example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># excerpted from above - workflow steps 3, 4, and 5&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Setup kubecfg CLI&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kingdonb/kubecfg/action@main&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kubecfg show&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kubecfg show manifests/example.jsonnet &amp;gt; output/production.yaml&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Prepare target branch&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>./ci/rake.sh deploy&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>While the remaining examples will be written to depend on &lt;code>kubecfg&lt;/code>, some use cases may prefer to use pure Jsonnet only as it is sandboxed and therefore safer. We plan to use the &lt;code>kubecfg&lt;/code> capability to take input from other sources, like variables and references, but also network-driven imports and functions.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># from above - substitute these steps in 03-release-manifests.yaml,&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># between &amp;#34;Checkout repo&amp;#34; and &amp;#34;Commit changes&amp;#34; to use plain Jsonnet instead of kubecfg&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>- &lt;span style="color:#062873;font-weight:bold">id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>jsonnet-render&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>alexdglover/jsonnet-render@v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">file&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>manifests/example.jsonnet&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">output_file&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>output/production.yaml&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">params&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>dryrun=true;env=prod&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Prepare target branch&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>./ci/rake.sh deploy&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>jsonnet-render&lt;/code> step is borrowed from another source, again find it on
&lt;a href="https://github.com/marketplace/actions/jsonnet-render" target="_blank">GitHub Actions Marketplace&lt;/a> for more information. For Tanka users, there is also
&lt;a href="https://github.com/letsbuilders/tanka-action" target="_blank">letsbuilders/tanka-action&lt;/a> which describes itself as heavily inspired by &lt;code>jsonnet-render&lt;/code>.&lt;/p>
&lt;div class="alert alert-primary" role="alert">
&lt;h4 class="alert-heading">The EndBug/add-and-commit action is used again&lt;/h4>
This time, with the help of &lt;code>rake.sh&lt;/code>, our change is staged into a different target branch. This is the same &lt;code>deploy&lt;/code> branch, regardless of which branch or tag the build comes from; any configured push event can trigger this workflow to trigger an update to the deploy branch.
&lt;/div>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># ./ci/rake.sh&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">set&lt;/span> -feux &lt;span style="color:#60a0b0;font-style:italic"># Usage: $0 &amp;lt;BRANCH&amp;gt; # Fails when BRANCH is not provided&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">BRANCH&lt;/span>&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#bb60d5">$1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># The output/ directory is listed in .gitignore, where jsonnet rendered output.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">pushd&lt;/span> output
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Fetch git branch &amp;#39;deploy&amp;#39; and run `git checkout deploy`&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>/usr/bin/git -c protocol.version&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#40a070">2&lt;/span> fetch &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> --no-tags --prune --progress --no-recurse-submodules &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> --depth&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#40a070">1&lt;/span> origin &lt;span style="color:#bb60d5">$BRANCH&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git checkout &lt;span style="color:#bb60d5">$BRANCH&lt;/span> --
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Prepare the output to commit by itself in the deploy branch&amp;#39;s root directory.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mv -f ./production.yaml ../ &lt;span style="color:#60a0b0;font-style:italic"># Overwrite any existing files (no garbage collection here)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git diff
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># All done (the commit will take place in the next action!)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#007020">popd&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Give this file &lt;code>chmod +x&lt;/code> before adding and committing; tailor this workflow to your needs. We render from a file &lt;code>manifests/example.jsonnet&lt;/code>, it can be anything. The output is a single K8s YAML file, &lt;code>production.yaml&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Commit changes&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>EndBug/add-and-commit@v7&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">add&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#39;production.yaml&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">branch&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>deploy&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">message&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;[ci skip] from ${{ steps.prep.outputs.VERSION }}&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">signoff&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is
&lt;a href="https://github.com/marketplace/actions/add-commit" target="_blank">Add &amp;amp; Commit&lt;/a> with a &lt;code>branch&lt;/code> option, to set the target branch. We&amp;rsquo;ve added a &lt;code>signoff&lt;/code> option as well here, to demonstrate another feature of this GitHub Action. There are many ways to use this workflow step. The link provides more information.&lt;/p>
&lt;p>The examples that follow can be copied and pasted into &lt;code>manifests/example.jsonnet&lt;/code>, then committed to the &lt;code>release&lt;/code> branch and pushed to GitHub in order to execute them.&lt;/p>
&lt;p>Pushing this to your repository will fail at first, unless and until a &lt;code>deploy&lt;/code> branch is created.&lt;/p>
&lt;p>Run &lt;code>git checkout --orphan deploy&lt;/code> to create a new empty HEAD in your repo.&lt;/p>
&lt;p>Run &lt;code>git reset&lt;/code> and &lt;code>git stash&lt;/code> to dismiss any files that were staged, then run &lt;code>git commit --allow-empty&lt;/code> to create an initial empty commit on the branch.&lt;/p>
&lt;p>Now you can copy and run these commands to create an empty branch:&lt;/p>
&lt;pre tabindex="0">&lt;code>#- Add a basic kustomization.yaml file
cat &amp;lt;&amp;lt;EOF &amp;gt; kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- production.yaml
EOF
#- Add a gitignore so output directory is created
cat &amp;lt;&amp;lt;EOF &amp;gt; .gitignore
/output/**
!/output/.keep
EOF
#- Add the .keep file
touch output/.keep
#- Push these files into an empty branch, and go back to main branch
git add output/.keep .gitignore kustomization.yaml
git commit -m&amp;#39;seed deploy kustomization.yaml for prod&amp;#39;
git push -u origin deploy
git checkout main
&lt;/code>&lt;/pre>&lt;p>On the main branch as well, create a &lt;code>.gitignore&lt;/code> and &lt;code>/output/.keep&lt;/code> for that branch too. We need to make sure that the &lt;code>output/&lt;/code> directory is present for writing whenever the Jsonnet workflow begins. The &lt;code>rake.sh&lt;/code> script sweeps files from the output into the root directory of the &lt;code>deploy&lt;/code> branch.&lt;/p>
&lt;p>Now run &lt;code>git checkout -b release; git push -u origin release&lt;/code> to trigger this action and see it working! 🤞🤞&lt;/p>
&lt;p>You may need to be sure that GitHub Actions are enabled on your repository before this will work.&lt;/p>
&lt;p>Read onward to see some basic as well as more advanced uses of &lt;code>kubecfg&lt;/code>.&lt;/p>
&lt;h5 id="jsonnet-configmap-with-envsubst">Jsonnet &lt;code>ConfigMap&lt;/code> with &lt;code>envsubst&lt;/code>&lt;/h5>
&lt;p>The next example &amp;ldquo;enforces,&amp;rdquo; or copies, a value from a &lt;code>configMap&lt;/code> from one namespace into many namespaces. This is done so that Kustomizations for each namespace can maintain similar config data in their reconciliations while staying DRY, with some configurations that reach across namespace boundaries.&lt;/p>
&lt;p>With Jsonnet, Kustomize, and Kustomization Controller&amp;rsquo;s &lt;code>postBuild&lt;/code> which uses &lt;code>envsubst&lt;/code>, there are usually a handful of different ways to accomplish the same task.&lt;/p>
&lt;p>This example demonstrates a feature called &lt;code>ext_vars&lt;/code> or
&lt;a href="https://jsonnet.org/ref/stdlib.html#ext_vars" target="_blank">External Variables&lt;/a> in the Jsonnet &lt;code>stdlib&lt;/code>.&lt;/p>
&lt;p>These examples assume (since no such protection has been presented) that nothing prevents &lt;code>production.yaml&lt;/code> from writing resources throughout multiple namespaces. This may be difficult or impossible to achieve depending on your environment.&lt;/p>
&lt;p>In order to permit a Kustomization to write into different namespaces, some RBAC configuration may be required.&lt;/p>
&lt;p>When you write a &lt;code>Kustomization&lt;/code> to apply this, be sure you are aware of whether or not you have set &lt;code>targetNamespace&lt;/code> on the Flux &lt;code>Kustomization&lt;/code> as it may override any namespace settings in the Jsonnet output. You may note similar configuration in the &lt;code>kustomization.yaml&lt;/code> we wrote into the deploy branch as described above, in the step for &lt;strong>Jsonnet Render Action&lt;/strong>.&lt;/p>
&lt;h5 id="external-variable-substitution">External Variable Substitution&lt;/h5>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>&lt;span style="">#&lt;/span> Any Old App Jsonnet example &lt;span style="color:#40a070">0.10&lt;/span>.&lt;span style="color:#40a070">1&lt;/span> &lt;span style="color:#666">-&lt;/span> manifests&lt;span style="color:#666">/&lt;/span>example.jsonnet
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local kube &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#4070a0">&amp;#39;https://github.com/bitnami-labs/kube-libsonnet/raw/73bf12745b86718083df402e89c6c903edd327d2/kube.libsonnet&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local example &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#4070a0">&amp;#39;example.libsonnet&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> version_configmap&lt;span style="color:#666">:&lt;/span> kube.ConfigMap(&lt;span style="color:#4070a0">&amp;#39;any-old-app-version&amp;#39;&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;prod&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> data&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> VERSION&lt;span style="color:#666">:&lt;/span> std.extVar(&lt;span style="color:#4070a0">&amp;#39;VERSION&amp;#39;&lt;/span>),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> flux_kustomization&lt;span style="color:#666">:&lt;/span> example.kustomization(&lt;span style="color:#4070a0">&amp;#39;any-old-app-prod&amp;#39;&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;flux-system&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;./flux-config/&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> postBuild&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> substituteFrom&lt;span style="color:#666">+:&lt;/span> [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kind&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;ConfigMap&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;any-old-app-version&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> flux_gitrepository&lt;span style="color:#666">:&lt;/span> example.gitrepository(&lt;span style="color:#4070a0">&amp;#39;any-old-app-prod&amp;#39;&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;flux-system&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;https://github.com/kingdonb/any_old_app&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The above jsonnet declaration &lt;code>example.jsonnet&lt;/code> will not complete without its neighbor &lt;code>example.libsonnet&lt;/code> (which can be found
&lt;a href="https://github.com/kingdonb/any_old_app/blob/release/0.10.1/manifests/example.libsonnet" target="_blank">linked here&lt;/a>.) This part of the example contains some boilerplate detail not meant to be copied, like the name &lt;code>any-old-app-prod&lt;/code> and the string &lt;code>'sops-gpg'&lt;/code> in &lt;code>decryption.secretRef&lt;/code> which should be changed to match your environment).&lt;/p>
&lt;p>If you visited the linked &lt;code>example.libsonnet&lt;/code> you may have noticed definitions for &lt;code>kustomization&lt;/code> and &lt;code>gitrepository&lt;/code> that are frankly pretty specific for a library function. They include details you wouldn&amp;rsquo;t expect to find in a vendor library, like a default git repository URL, and a default hardcoded ref to the name of our Source gitrepository.&lt;/p>
&lt;p>This is &lt;strong>our library file&lt;/strong>, so it can have our own implementation-specific details in it if we want to include them. Now, the power of Jsonnet is visible; we get to decide which configuration needs to be exposed in our main &lt;code>example.jsonnet&lt;/code> file, and which parameters are defaults provided by the library, that can be treated like boilerplate and re-defined however we want.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>&lt;span style="">data+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="">VERSION:&lt;/span> &lt;span style="">std.extVar(&amp;#39;VERSION&amp;#39;),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}&lt;span style="">,&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is &lt;code>std.extVar&lt;/code> from &lt;code>ext_vars&lt;/code> mentioned earlier. Arrange for the version to be passed in through the GitHub Actions workflow:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># adapted from above - 03-release-manifests.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kubecfg show&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kubecfg show -V VERSION=${{ steps.prep.outputs.VERSION }} manifests/example.jsonnet &amp;gt; output/production.yaml&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The neighbor &lt;code>example.libsonnet&lt;/code> file contains some boring (but necessary) boilerplate, so that &lt;code>kubecfg&lt;/code> can fulfill this jsonnet, to generate and commit the full Kustomize-ready YAML into a &lt;code>deploy&lt;/code> branch as specified in the workflow. (The &lt;code>Kustomization&lt;/code> for this example is provided
&lt;a href="https://github.com/kingdonb/csh-flux/commit/7c3f1e62e2a87a2157bc9a22db4f913cc30dc12e#diff-f6ebc9688433418f0724f3545c96c301f029fd5a15847b824eab04545e057e84" target="_blank">from my fleet-infra repo here&lt;/a>. Personalize and adapt this for use with your own application or manifest generations.)&lt;/p>
&lt;p>The values provided are for example only and should be personalized, or restructured/rewritten completely to suit your preferred template values and instances. For more idiomatic examples written recently by actual Jsonnet pros, the
&lt;a href="https://tanka.dev/tutorial/jsonnet" target="_blank">Tanka - Using Jsonnet tutorial&lt;/a> is great, and so is
&lt;a href="https://tanka.dev/tutorial/parameters" target="_blank">Tanka - Parameterizing&lt;/a> which I&amp;rsquo;ll call out specifically for the &lt;code>_config::&lt;/code> object example that is decidedly more elegant than my version of parameter passing.&lt;/p>
&lt;p>If you&amp;rsquo;ve not previously used Jsonnet before, then you might be wondering about that code example you just read and that&amp;rsquo;s OK! If you &lt;strong>have&lt;/strong> previously used Jsonnet and already know what idiomatic Jsonnet looks like, you might be wondering too&amp;hellip; you can probably tell I (author, Kingdon) practically haven&amp;rsquo;t ever written a lick of Jsonnet before today.&lt;/p>
&lt;p>These examples are going to get progressively more advanced as I learn Jsonnet while I go. At this point I already think it&amp;rsquo;s pretty cool and I barely know how to use it, but I am starting to understand what type of problems people are using it to solve.&lt;/p>
&lt;p>ConfigMap values are not treated as secret data, so there is no encryption to contend with; this makes for what seems like a good first example. Jsonnet enthusiasts, please forgive my newness. I am sure that my interpretation of how to write Jsonnet is most likely not optimal or idiomatic.&lt;/p>
&lt;p>Above we showed how to pass in a string from our build pipeline, and use it to write back generated Jsonnet manifests into a commit.&lt;/p>
&lt;h5 id="make-two-environments">Make Two Environments&lt;/h5>
&lt;p>Here&amp;rsquo;s a second example, defining two environments in separate namespaces, instead of just one:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>&lt;span style="">#&lt;/span> Any Old App Jsonnet example &lt;span style="color:#40a070">0.10&lt;/span>.&lt;span style="color:#40a070">2&lt;/span>&lt;span style="color:#666">-&lt;/span>alpha1 &lt;span style="color:#666">-&lt;/span> manifests&lt;span style="color:#666">/&lt;/span>example.jsonnet
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="">#&lt;/span> Replicate a section &lt;span style="color:#007020;font-weight:bold">of&lt;/span> config and change nothing &lt;span style="color:#007020;font-weight:bold">else&lt;/span> about it
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> version_configmap&lt;span style="color:#666">:&lt;/span> kube.ConfigMap(&lt;span style="color:#4070a0">&amp;#39;any-old-app-version&amp;#39;&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;flux-system&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> data&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> VERSION&lt;span style="color:#666">:&lt;/span> std.extVar(&lt;span style="color:#4070a0">&amp;#39;VERSION&amp;#39;&lt;/span>),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> test_flux_kustomization&lt;span style="color:#666">:&lt;/span> example.kustomization(&lt;span style="color:#4070a0">&amp;#39;any-old-app-test&amp;#39;&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;flux-system&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;./flux-config/&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> postBuild&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> substituteFrom&lt;span style="color:#666">+:&lt;/span> [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kind&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;ConfigMap&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;any-old-app-version&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> targetNamespace&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;test-tenant&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> prod_flux_kustomization&lt;span style="color:#666">:&lt;/span> example.kustomization(&lt;span style="color:#4070a0">&amp;#39;any-old-app-prod&amp;#39;&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;flux-system&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;./flux-config/&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> postBuild&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> substituteFrom&lt;span style="color:#666">+:&lt;/span> [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kind&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;ConfigMap&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;any-old-app-version&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> targetNamespace&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;prod-tenant&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> flux_gitrepository&lt;span style="color:#666">:&lt;/span> example.gitrepository(&lt;span style="color:#4070a0">&amp;#39;any-old-app-prod&amp;#39;&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;flux-system&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;https://github.com/kingdonb/any_old_app&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this example, some front-matter was omitted for brevity. Wait, what? (There&amp;rsquo;s nothing brief about this example, it&amp;rsquo;s extra-verbose!)&lt;/p>
&lt;p>&amp;ldquo;I thought Jsonnet was supposed to be DRY.&amp;rdquo; Be gentle, refactoring is a methodical and deliberate process. We simply copied the original one environment into two environments, test and prod, which differ only in name.&lt;/p>
&lt;p>In the next example, we will subtly change one of them to be configured differently from the other.&lt;/p>
&lt;h5 id="change-something-and-refactor">Change Something and Refactor&lt;/h5>
&lt;p>Note the string &amp;lsquo;flux-system&amp;rsquo; only occurs once now, having been factored into a variable &lt;code>config_ns&lt;/code>. These are some basic abstractions in Jsonnet that we can use to start to DRY up our source manifests.&lt;/p>
&lt;p>Again, practically nothing changes functionally, this still does exactly the same thing. With another refactoring, we can express this manifest more concisely, thanks to a new library function we can invent, named &lt;code>example.any_old_app&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>&lt;span style="">#&lt;/span> Any Old App Jsonnet example &lt;span style="color:#40a070">0.10&lt;/span>.&lt;span style="color:#40a070">2&lt;/span>&lt;span style="color:#666">-&lt;/span>alpha4 &lt;span style="color:#666">-&lt;/span> manifests&lt;span style="color:#666">/&lt;/span>example.jsonnet
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="">#&lt;/span> Make something different between test and prod
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> version_configmap&lt;span style="color:#666">:&lt;/span> kube.ConfigMap(&lt;span style="color:#4070a0">&amp;#39;any-old-app-version&amp;#39;&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace&lt;span style="color:#666">:&lt;/span> config_ns,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> data&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> VERSION&lt;span style="color:#666">:&lt;/span> std.extVar(&lt;span style="color:#4070a0">&amp;#39;VERSION&amp;#39;&lt;/span>),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> test_flux_kustomization&lt;span style="color:#666">:&lt;/span> example.any_old_app(&lt;span style="color:#4070a0">&amp;#39;test&amp;#39;&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> prune&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#007020;font-weight:bold">true&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> prod_flux_kustomization&lt;span style="color:#666">:&lt;/span> example.any_old_app(&lt;span style="color:#4070a0">&amp;#39;prod&amp;#39;&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> prune&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#007020;font-weight:bold">false&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> flux_gitrepository&lt;span style="color:#666">:&lt;/span> example.gitrepository(&lt;span style="color:#4070a0">&amp;#39;any-old-app-prod&amp;#39;&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace&lt;span style="color:#666">:&lt;/span> config_ns,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;https://github.com/kingdonb/any_old_app&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Two things have changed to make this refactoring of the config differ from the first version. Hopefully you&amp;rsquo;ll notice it&amp;rsquo;s a lot shorter.&lt;/p>
&lt;p>Redundant strings have been collapsed into a variable, and more boilerplate has been moved into the library.&lt;/p>
&lt;p>This refactored state is perhaps the most obvious to review, and most intentionally clear about its final intent to the reader. Hopefully you noticed the original environments were identical (or if your eyes glossed over because of the wall of values, you&amp;rsquo;ve at least taken my word for it.)&lt;/p>
&lt;p>But now, these two differ. We&amp;rsquo;re creating two configurations for &lt;code>any_old_app&lt;/code>, named &lt;code>test&lt;/code> and &lt;code>prod&lt;/code>. One of them has &lt;code>prune&lt;/code> enabled, the test environment, and &lt;code>prod&lt;/code> is set more conservatively to prevent accidental deletions, with a setting of &lt;code>prune: false&lt;/code>.&lt;/p>
&lt;p>Since the two environments should each differ only by the boolean setting of &lt;code>spec.prune&lt;/code>, we can now pack up and hide away the remainder of the config in with the rest of the boilerplate.&lt;/p>
&lt;p>Hiding the undifferentiated boilerplate in a library makes it easier to detect and observe this difference in a quick visual review.&lt;/p>
&lt;p>Here&amp;rsquo;s the new library function&amp;rsquo;s definition:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>any_old_app(environment)&lt;span style="color:#666">::&lt;/span> self.kustomization(&lt;span style="color:#4070a0">&amp;#39;any-old-app-&amp;#39;&lt;/span> &lt;span style="color:#666">+&lt;/span> environment) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;flux-system&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> path&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;./flux-config/&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> postBuild&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> substituteFrom&lt;span style="color:#666">+:&lt;/span> [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kind&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;ConfigMap&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;any-old-app-version&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> targetNamespace&lt;span style="color:#666">:&lt;/span> environment &lt;span style="color:#666">+&lt;/span> &lt;span style="color:#4070a0">&amp;#39;-tenant&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>},
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This excerpt is taken from
&lt;a href="https://github.com/kingdonb/any_old_app/blob/release/0.10.2/manifests/example.libsonnet#L47-L63" target="_blank">the 10.2 release version&lt;/a> of &lt;code>example.libsonnet&lt;/code>, where you can also read the specific definition of &lt;code>kustomization&lt;/code> that is invoked with the expression &lt;code>self.kustomization('any-old-app-' + environment)&lt;/code>.&lt;/p>
&lt;p>The evolution of this jsonnet snippet has gone from &lt;em>unnecessarily verbose&lt;/em> to &lt;strong>perfect redundant clarity&lt;/strong>. I say redundant, but I&amp;rsquo;m actually fine with this exactly the way it is. I think, if nothing further changes, we already have met the best way to express this particular manifest with Jsonnet.&lt;/p>
&lt;p>But given that config will undoubtedly have to change as the differing requirements of our development teams and their environments grow, this perfect clarity unfortunately can&amp;rsquo;t last forever in its current form. It will have to scale.&lt;/p>
&lt;p>Notice that strings and repeated invocations of &lt;code>any_old_app&lt;/code> are written with parallel structure and form, but there&amp;rsquo;s nothing explicit linking them.&lt;/p>
&lt;p>The object-oriented programmer in me can&amp;rsquo;t help but ask now, &amp;ldquo;what happens when we need another copy of the environment, this time slightly more different than those two, and &amp;hellip; how about two more after that, (and actually, can we really get by with only ten environments?)&amp;rdquo; — I am inclined towards thinking of this repeated structure as a sign that an object cries out, waiting to be recognized and named, and defined, (and refactored and defined again.)&lt;/p>
&lt;h5 id="list-comprehension">List Comprehension&lt;/h5>
&lt;p>So get ready for &lt;em>obfuscated nightmare mode&lt;/em>, (which is the name we thoughtfully reserved for the
&lt;a href="https://github.com/kingdonb/any_old_app/blob/release/0.10.2/manifests/example.jsonnet" target="_blank">best and final version&lt;/a> of the example,) shown below.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>&lt;span style="">#&lt;/span> Any Old App Jsonnet example &lt;span style="color:#40a070">0.10&lt;/span>.&lt;span style="color:#40a070">2&lt;/span> &lt;span style="color:#666">-&lt;/span> manifests&lt;span style="color:#666">/&lt;/span>example.jsonnet
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// This is a simple manifest generation example to demo some simple tasks that
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// can be automated through Flux, with Flux configs rehydrated through Jsonnet.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// This example uses kube.libsonnet from Bitnami. There are other
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// Kubernetes libraries available, or write your own!
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>local kube &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#4070a0">&amp;#39;https://github.com/bitnami-labs/kube-libsonnet/raw/73bf12745b86718083df402e89c6c903edd327d2/kube.libsonnet&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// The declaration below adds configuration to a more verbose base, defined in
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// more detail at the neighbor libsonnet file here:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>local example &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#4070a0">&amp;#39;example.libsonnet&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local kubecfg &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#4070a0">&amp;#39;kubecfg.libsonnet&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local kustomize &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#4070a0">&amp;#39;kustomize.libsonnet&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local config_ns &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;flux-system&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local flux_config &lt;span style="color:#666">=&lt;/span> [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kube.ConfigMap(&lt;span style="color:#4070a0">&amp;#39;any-old-app-version&amp;#39;&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> data&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> VERSION&lt;span style="color:#666">:&lt;/span> std.extVar(&lt;span style="color:#4070a0">&amp;#39;VERSION&amp;#39;&lt;/span>),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> example.gitrepository(&lt;span style="color:#4070a0">&amp;#39;any-old-app-prod&amp;#39;&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> url&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;https://github.com/kingdonb/any_old_app&amp;#39;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>] &lt;span style="color:#666">+&lt;/span> kubecfg.parseYaml(importstr &lt;span style="color:#4070a0">&amp;#39;examples/configMap.yaml&amp;#39;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local kustomization &lt;span style="color:#666">=&lt;/span> kustomize.applyList([
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kustomize.namespace(config_ns),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>]);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local kustomization_output &lt;span style="color:#666">=&lt;/span> std.map(kustomization, flux_config);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{ flux_config&lt;span style="color:#666">:&lt;/span> kustomization_output } &lt;span style="color:#666">+&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> local items &lt;span style="color:#666">=&lt;/span> [&lt;span style="color:#4070a0">&amp;#39;test&amp;#39;&lt;/span>, &lt;span style="color:#4070a0">&amp;#39;prod&amp;#39;&lt;/span>],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> joined&lt;span style="color:#666">:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> [ns &lt;span style="color:#666">+&lt;/span> &lt;span style="color:#4070a0">&amp;#39;_flux_kustomization&amp;#39;&lt;/span>]&lt;span style="color:#666">:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> data&lt;span style="color:#666">:&lt;/span> example.any_old_app(ns) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> prune&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> ns &lt;span style="color:#666">==&lt;/span> &lt;span style="color:#4070a0">&amp;#39;prod&amp;#39;&lt;/span> then &lt;span style="color:#007020;font-weight:bold">false&lt;/span> &lt;span style="color:#007020;font-weight:bold">else&lt;/span> &lt;span style="color:#007020;font-weight:bold">true&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">for&lt;/span> ns &lt;span style="color:#007020;font-weight:bold">in&lt;/span> items
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// Credit:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> &lt;span style="color:#60a0b0;font-style:italic">// https://groups.google.com/g/jsonnet/c/ky6sjYj4UZ0/m/d4lZxWbhAAAJ
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> &lt;span style="color:#60a0b0;font-style:italic">// thanks Dave for showing how to do something like this in Jsonnet
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is the sixth revision of this example, (some have been omitted from the story, but they are
&lt;a href="https://github.com/kingdonb/any_old_app/releases?after=0.10.2-alpha5" target="_blank">in Git history&lt;/a>.) I think it&amp;rsquo;s really perfect now. If you&amp;rsquo;re a programmer, I think, this version is perhaps much clearer. That&amp;rsquo;s why I called it &lt;em>obfuscated nightmare mode&lt;/em>, right? (I&amp;rsquo;m a programmer, I swear.)&lt;/p>
&lt;p>The &lt;code>examples/configMap.yaml&lt;/code> file can be found
&lt;a href="https://github.com/kingdonb/any_old_app/blob/release/0.10.2/manifests/examples/configMap.yaml" target="_blank">in the 0.10.2 tag&lt;/a> of &lt;code>kingdonb/any_old_app&lt;/code>, it is vestigial and does not serve any functional purpose in this example, except for showing how to compose Jsonnet objects with parsed YAML from a file.&lt;/p>
&lt;p>You should note that kubecfg&amp;rsquo;s &lt;code>kubecfg.parseYaml&lt;/code> method always returns an array, even when the &lt;code>importstr&lt;/code> input file only contains a single YAML document. Jsonnet arrays, like strings, can be easily added together with a familiar &lt;code>+&lt;/code> operator.&lt;/p>
&lt;p>Jsonnet objects can also be added to other objects, composing their fields from smaller objects into larger ones. In the example above, we have added the &lt;code>flux_config&lt;/code> object to a collection of &lt;code>AnyOldApp&lt;/code> objects, a list comprehension from our environments. This is necessary and important because a Jsonnet program or library must always return a single object.&lt;/p>
&lt;p>I&amp;rsquo;m trying to learn Jsonnet as fast as I can, I hope you&amp;rsquo;re still with me and if not, don&amp;rsquo;t worry. Where did all of this programming come from? (And what&amp;rsquo;s a list comprehension?) It really doesn&amp;rsquo;t matter.&lt;/p>
&lt;p>The heavy lifting libraries for this example are from
&lt;a href="https://github.com/anguslees/kustomize-libsonnet" target="_blank">anguslees/kustomize-libsonnet&lt;/a>, which implements some basic primitives of Kustomize in Jsonnet. YAML parser is provided by
&lt;a href="https://github.com/bitnami/kubecfg/blob/master/lib/kubecfg.libsonnet#L25" target="_blank">bitnami/kubecfg&lt;/a>, and the Jsonnet implementations of Kubernetes primitives by
&lt;a href="https://github.com/bitnami-labs/kube-libsonnet" target="_blank">bitnami-labs/kube-libsonnet&lt;/a>.&lt;/p>
&lt;p>It is a matter of taste whether you consider from above the first, second, or third example to be better stylistically. It is a matter of taste and circumstances, to put a finer point on it. They each have strengths and weaknesses, depending mostly on whatever changes we will have to make to them next.&lt;/p>
&lt;p>We can compare these three versions to elucidate the intent of the programmatically most expressive version which followed the other two. If you&amp;rsquo;re new at this, you may try to explain how these three examples are similar, and also how they differ. Follow the explanation below for added clarity.&lt;/p>
&lt;p>If you haven&amp;rsquo;t studied Jsonnet, this last version may daunt you with its complexity. The fact is YAML is a document store and Jsonnet is a programming language. This complexity is exactly what we came here for, we want our configuration language to be more powerful! Bring on more complex Jsonnet examples!&lt;/p>
&lt;h4 id="breaking-it-down">Breaking It Down&lt;/h4>
&lt;p>We define a configmap and a gitrepository (in Jsonnet), then put it together with another configmap (from plain YAML). That&amp;rsquo;s called &lt;code>flux_config&lt;/code>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>local kustomization &lt;span style="color:#666">=&lt;/span> kustomize.applyList([
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kustomize.namespace(config_ns),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>]);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local kustomization_output &lt;span style="color:#666">=&lt;/span> std.map(kustomization, flux_config);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This little diddy (above) has the same effect as a Kustomization based on the following instruction to &lt;code>kustomize build&lt;/code>, (except it&amp;rsquo;s all jsonnet):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kustomize.config.k8s.io/v1beta1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Kustomization&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${config_ns}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&amp;hellip; setting the namespace for all objects in &lt;code>flux_config&lt;/code> to the value of &lt;code>config_ns&lt;/code>.&lt;/p>
&lt;p>Next, we join it together with a list comprehension (at least I think that&amp;rsquo;s what this is called):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>local items &lt;span style="color:#666">=&lt;/span> [&lt;span style="color:#4070a0">&amp;#39;test&amp;#39;&lt;/span>, &lt;span style="color:#4070a0">&amp;#39;prod&amp;#39;&lt;/span>],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>joined&lt;span style="color:#666">:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> [ns &lt;span style="color:#666">+&lt;/span> &lt;span style="color:#4070a0">&amp;#39;_flux_kustomization&amp;#39;&lt;/span>]&lt;span style="color:#666">:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> data&lt;span style="color:#666">:&lt;/span> example.any_old_app(ns) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> spec&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> prune&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> ns &lt;span style="color:#666">==&lt;/span> &lt;span style="color:#4070a0">&amp;#39;prod&amp;#39;&lt;/span> then &lt;span style="color:#007020;font-weight:bold">false&lt;/span> &lt;span style="color:#007020;font-weight:bold">else&lt;/span> &lt;span style="color:#007020;font-weight:bold">true&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">for&lt;/span> ns &lt;span style="color:#007020;font-weight:bold">in&lt;/span> items
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>},
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Two &lt;code>any_old_app&lt;/code> templates are invoked programmatically, with different properties and names, and in target namespaces that are based on the environment names. They go on the end of the document list, and Jsonnet renders them alongside of the others, in various namespaces.&lt;/p>
&lt;p>This is the same technique as in &lt;code>01-manifest-generate.yaml&lt;/code>, only this time with Jsonnet and &lt;code>kubecfg&lt;/code> instead of &lt;code>sed&lt;/code>, (and what a difference it makes!)&lt;/p>
&lt;p>This is the foundation for some real release machinery for your applications, this is not just a bunch of shell scripts. Whenever any commit hits the &lt;code>release&lt;/code> branch, or when any tag in the form &lt;code>release/*&lt;/code> is pushed, the repo is configured to push generated manifest changes to a &lt;code>deploy&lt;/code> branch.&lt;/p>
&lt;p>This behavior is self-contained within the example &lt;code>any_old_app&lt;/code> repository in these examples.&lt;/p>
&lt;p>We can use GitHub Actions and Jsonnet to populate parameters through ConfigMap values or with &lt;code>extVars&lt;/code>, and at the same time, apply &lt;code>Kustomization&lt;/code> and &lt;code>GitRepository&lt;/code> as new sync infrastructure for the &lt;code>deploy&lt;/code> branch with dependencies on those ConfigMaps. The &lt;code>Kustomization&lt;/code> refers to the configmap and makes the &lt;code>VERSION&lt;/code> or &lt;code>GIT_SHA&lt;/code> variable available as a &lt;code>postBuild&lt;/code> substitution, with values pulled from that same configmap we just applied.&lt;/p>
&lt;p>Later, we can repeat this process with a SOPS encrypted secret.&lt;/p>
&lt;p>The process is not very different, though some of the boilerplate is longer, we&amp;rsquo;ve already learned to pack away boilerplate. Copying and renaming encrypted secrets within the same cluster is possible wherever cluster operators are permitted to both decrypt and encrypt them with the decryption provider.&lt;/p>
&lt;p>A credential at &lt;code>spec.decryption.secretRef&lt;/code> holds the key for decryption. Without additional configuration secrets can usually be copied freely around the cluster, as it is possible to decrypt them freely anywhere the decryption keys are made available.&lt;/p>
&lt;h5 id="copy-configmaps">Copy &lt;code>ConfigMap&lt;/code>s&lt;/h5>
&lt;p>Assume that each namespace will be separately configured as a tenant by itself somehow later, and that each tenant performs its own git reconciliation within the tenant namespace. That config is out of scope for this example. We are only interested in briefly demonstrating some Jsonnet use cases here.&lt;/p>
&lt;p>The app version to install is maintained in a &lt;code>ConfigMap&lt;/code> in each namespace based on our own decision logic. This can be implemented as a human operator who goes in and updates this variable&amp;rsquo;s value before release time.&lt;/p>
&lt;p>This Jsonnet creates from a list of namespaces, and injects a &lt;code>ConfigMap&lt;/code> into each namespace,
&lt;a href="https://github.com/kingdonb/any_old_app/blob/release/0.10.3/manifests/example.jsonnet" target="_blank">another example.jsonnet&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>&lt;span style="">#&lt;/span> Any Old App Jsonnet example &lt;span style="color:#40a070">0.10&lt;/span>.&lt;span style="color:#40a070">3&lt;/span> &lt;span style="color:#666">-&lt;/span> manifests&lt;span style="color:#666">/&lt;/span>example.jsonnet
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local release_config &lt;span style="color:#666">=&lt;/span> kube.ConfigMap(&lt;span style="color:#4070a0">&amp;#39;any-old-app-version&amp;#39;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local namespace_list &lt;span style="color:#666">=&lt;/span> [&lt;span style="color:#4070a0">&amp;#39;prod&amp;#39;&lt;/span>, &lt;span style="color:#4070a0">&amp;#39;stg&amp;#39;&lt;/span>, &lt;span style="color:#4070a0">&amp;#39;qa&amp;#39;&lt;/span>, &lt;span style="color:#4070a0">&amp;#39;uat&amp;#39;&lt;/span>, &lt;span style="color:#4070a0">&amp;#39;dev&amp;#39;&lt;/span>];
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local release_version &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;0.10.3&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local latest_candidate &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;0.10.3-alpha1&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> [ns &lt;span style="color:#666">+&lt;/span> &lt;span style="color:#4070a0">&amp;#39;_tenant&amp;#39;&lt;/span>]&lt;span style="color:#666">:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> [ns &lt;span style="color:#666">+&lt;/span> &lt;span style="color:#4070a0">&amp;#39;_namespace&amp;#39;&lt;/span>]&lt;span style="color:#666">:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace&lt;span style="color:#666">:&lt;/span> kube.Namespace(ns),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> [ns &lt;span style="color:#666">+&lt;/span> &lt;span style="color:#4070a0">&amp;#39;_configmap&amp;#39;&lt;/span>]&lt;span style="color:#666">:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> version_data&lt;span style="color:#666">:&lt;/span> release_config {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> metadata&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> namespace&lt;span style="color:#666">:&lt;/span> ns,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> data&lt;span style="color:#666">+:&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> VERSION&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> ns &lt;span style="color:#666">==&lt;/span> &lt;span style="color:#4070a0">&amp;#39;prod&amp;#39;&lt;/span> &lt;span style="color:#666">||&lt;/span> ns &lt;span style="color:#666">==&lt;/span> &lt;span style="color:#4070a0">&amp;#39;stg&amp;#39;&lt;/span> then release_version &lt;span style="color:#007020;font-weight:bold">else&lt;/span> latest_candidate,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">for&lt;/span> ns &lt;span style="color:#007020;font-weight:bold">in&lt;/span> namespace_list
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this example, we have set up an additional 3 namespaces and assumed that a Flux Kustomization is provided some other way. The deploy configuration of all 5 environments is maintained here, in a single deploy config.&lt;/p>
&lt;p>Imagine that two policies should exist for promoting releases into environments. The environments for &lt;code>dev&lt;/code>elopment, &lt;code>U&lt;/code>ser &lt;code>A&lt;/code>cceptance &lt;code>T&lt;/code>esting (&lt;code>uat&lt;/code>), and &lt;code>Q&lt;/code>uality &lt;code>A&lt;/code>ssurance (&lt;code>qa&lt;/code>) can all be primed with the latest release candidate build at any given time.&lt;/p>
&lt;p>This is perhaps an excessive amount of formality for an open source or cloud-native project, though readers working in regulated environments may recognize this familiar pattern.&lt;/p>
&lt;p>This example will possibly fail to apply with the recommended validations enabled, failing with errors that you can review by running &lt;code>flux get kustomization&lt;/code> in your flux namespace, like these:&lt;/p>
&lt;pre tabindex="0">&lt;code>validation failed: namespace/dev created (server dry run)
namespace/prod created (server dry run)
...
Error from server (NotFound): error when creating &amp;#34;14f54b89-2456-4c15-862e-34670dfcda79.yaml&amp;#34;: namespaces &amp;#34;dev&amp;#34; not found
Error from server (NotFound): error when creating &amp;#34;14f54b89-2456-4c15-862e-34670dfcda79.yaml&amp;#34;: namespaces &amp;#34;prod&amp;#34; not found
&lt;/code>&lt;/pre>&lt;p>As you can perhaps see, the problem is that objects created within the namespace are not valid before creating the namespace. You can disable the validation temporarily by adding a setting &lt;code>validation: none&lt;/code> to your Flux Kustomization to get past this error.&lt;/p>
&lt;p>In the deployment configuration above, both &lt;code>prod&lt;/code> and staging (&lt;code>stg&lt;/code>) are kept in sync with the latest release (not pre-release).&lt;/p>
&lt;p>Left as an exercise to the reader, we can also ask next: is it possible to supplement this configuration with a Flagger canary so that updates to the production config are able to be manually verified in the staging environment before they are promoted into Production?&lt;/p>
&lt;p>(Hint: Look at the
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flagger/usage/webhooks/#manual-gating">Manual Gating&lt;/a> feature of Flagger.)&lt;/p>
&lt;h5 id="copy-secrets">Copy &lt;code>Secret&lt;/code>s&lt;/h5>
&lt;p>This example writes the same &lt;code>secretRef&lt;/code> into many &lt;code>HelmReleases&lt;/code>, to provide for the cluster to be able to use the same &lt;code>imagePullSecret&lt;/code> across several &lt;code>Deployments&lt;/code> in a namespace. It is a common problem that &lt;code>jsonnet&lt;/code> can solve quite handily, without repeating the &lt;code>Secret&lt;/code> name over and over as a string.&lt;/p>
&lt;p>Because we have decided to create tenants for each namespace, now is a good time to mention
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/cmd/flux_create_tenant">flux create tenant&lt;/a>.&lt;/p>
&lt;p>We can take the output of &lt;code>flux create tenant prod --with-namespace prod --export&lt;/code> and use it to create &lt;code>manifests/examples/tenant.yaml&lt;/code>. Perhaps in a full implementation, we would create a tenant library function and call it many times to create our tenants.&lt;/p>
&lt;p>For this example, you may discard the &lt;code>Namespace&lt;/code> and/or &lt;code>ClusterRoleBinding&lt;/code> as they are not needed. Here, we actually just need a ServiceAccount to patch.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#0e84b5;font-weight:bold">---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># manifests/examples/tenant.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ServiceAccount&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">labels&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">toolkit.fluxcd.io/tenant&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>prod&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>prod&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>prod&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A namespace will be created by our Jsonnet example code instead (or you may comment this line out in the jsonnet below, if you are already working within a tenant.) The ClusterRoleBinding, or a more restrictive RoleBinding, is important for a functioning Flux tenant installation, but it is not needed for this example.&lt;/p>
&lt;p>(For more information on multi-tenancy, read the
&lt;a href="https://github.com/fluxcd/flux2-multi-tenancy" target="_blank">Flux 2 Multi-Tenancy Guide&lt;/a>.)&lt;/p>
&lt;p>We make an image pull secret with some docker registry credentials, for the purpose of completing the example. This is just for an example, it can be any secret that you want to replicate through several namespaces in the cluster with Jsonnet.&lt;/p>
&lt;pre tabindex="0">&lt;code>kubectl create secret docker-registry prod-docker-pull-secret \
--namespace=prod \
--docker-username=youruser --docker-password=secretpassword \
--docker-email=required@example.org --dry-run=client -oyaml \
&amp;gt; manifests/examples/sops-image-pull-secret.yaml
sops -e -i manifests/examples/sops-image-pull-secret.yaml
&lt;/code>&lt;/pre>&lt;p>If you are not familiar with SOPS encryption, you should complete the
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/guides/mozilla-sops/">Mozilla SOPS&lt;/a> Flux guide before replicating this example, and internalize all of the concepts explained there.&lt;/p>
&lt;p>You will need to have configured your cluster&amp;rsquo;s Kustomization with a decryption provider and decryption keys to enable the inclusion of encrypted secrets in your config repos. It is not safe to write unencrypted secrets into your git repository, and this should be avoided at all costs even if your repository is kept private.&lt;/p>
&lt;p>This final Jsonnet example is presented in context as a working reference in the &lt;code>any_old_app&lt;/code> repository, once again as
&lt;a href="https://github.com/kingdonb/any_old_app/blob/release/0.10.4/manifests/example.jsonnet" target="_blank">example.jsonnet&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>&lt;span style="">#&lt;/span> Any Old App Jsonnet example &lt;span style="color:#40a070">0.10&lt;/span>.&lt;span style="color:#40a070">4&lt;/span> &lt;span style="color:#666">-&lt;/span> manifests&lt;span style="color:#666">/&lt;/span>example.jsonnet
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local kubecfg &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#4070a0">&amp;#39;kubecfg.libsonnet&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local kustomize &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#007020;font-weight:bold">import&lt;/span> &lt;span style="color:#4070a0">&amp;#39;kustomize.libsonnet&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local tenant &lt;span style="color:#666">=&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kubecfg.parseYaml(importstr &lt;span style="color:#4070a0">&amp;#39;examples/tenant.yaml&amp;#39;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local pull_secrets &lt;span style="color:#666">=&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kubecfg.parseYaml(importstr &lt;span style="color:#4070a0">&amp;#39;examples/sops-image-pull-secret.yaml&amp;#39;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local prod_ns &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;prod&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local staging_ns &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;stg&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>local image_pull_secret &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;prod-docker-pull-secret&amp;#39;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// Set the Image Pull Secret on each ServiceAccount
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>local updateConfig(o) &lt;span style="color:#666">=&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#007020;font-weight:bold">if&lt;/span> o.kind &lt;span style="color:#666">==&lt;/span> &lt;span style="color:#4070a0">&amp;#39;ServiceAccount&amp;#39;&lt;/span> then o {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> imagePullSecrets&lt;span style="color:#666">:&lt;/span> [{ name&lt;span style="color:#666">:&lt;/span> image_pull_secret }],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> } &lt;span style="color:#007020;font-weight:bold">else&lt;/span> o
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// Create a namespace, and add to it Namespace and Secret
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>local prod_tenant &lt;span style="color:#666">=&lt;/span> [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kube.Namespace(prod_ns),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>] &lt;span style="color:#666">+&lt;/span> pull_secrets &lt;span style="color:#666">+&lt;/span> tenant;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// Prod kustomization - apply the updateConfig
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>local prod_kustomization &lt;span style="color:#666">=&lt;/span> kustomize.applyList([
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> updateConfig,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>]);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// Stg kustomization - apply the updateConfig and &amp;#34;stg&amp;#34; ns
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>local staging_kustomization &lt;span style="color:#666">=&lt;/span> kustomize.applyList([
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> updateConfig,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kustomize.namespace(staging_ns),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>]);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// Include both kustomizations in the Jsonnet object
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> prod&lt;span style="color:#666">:&lt;/span> std.map(prod_kustomization, prod_tenant),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> stg&lt;span style="color:#666">:&lt;/span> std.map(staging_kustomization, prod_tenant),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>kubecfg.parseYaml&lt;/code> instruction returns a list of Jsonnet objects. Our own Jsonnet closely mirrors the
&lt;a href="https://github.com/anguslees/kustomize-libsonnet/blob/master/example.jsonnet" target="_blank">example provided&lt;/a> by anguslees, with a few differences.&lt;/p>
&lt;p>The power of kubecfg is well illustrated through this example inspired by
&lt;a href="https://github.com/anguslees/kustomize-libsonnet" target="_blank">anguslees/kustomize-libsonnet&lt;/a>. We parse several YAML files and make several minor updates to them. It really doesn&amp;rsquo;t matter if one document involved is a secret, or that its data is encrypted by SOPS.&lt;/p>
&lt;p>If you are coming to Mozilla SOPS support in Flux v2, having used the SealedSecrets controller before when it was recommended in Flux v1, then you are probably surprised that this works. SOPS does not encrypt secret metadata when used with Flux&amp;rsquo;s Kustomize Controller integration, which makes examples like this one possible.&lt;/p>
&lt;p>The ServiceAccount is a part of Flux&amp;rsquo;s &lt;code>tenant&lt;/code> configuration, and a fundamental concept of Kubernetes RBAC. If this concept is still new to you, read more in the
&lt;a href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-multiple-service-accounts" target="_blank">Kubernetes docs on Using Service Accounts&lt;/a>.&lt;/p>
&lt;p>The other fundamental concept to understand is a Namespace.&lt;/p>
&lt;p>Secrets are namespaced objects, and ordinary users with tenant privileges cannot reach outside of their namespace. If tenants should manage a Flux Kustomization within their own namespace boundaries, then a &lt;code>sops-gpg&lt;/code> secret must be present in the Namespace with the Kustomization. Cross-namespace secret refs are not supported.&lt;/p>
&lt;p>However, any principal with access to read a &lt;code>sops-gpg&lt;/code> secret can decrypt any data that are encrypted for it.&lt;/p>
&lt;p>Each ServiceAccount can list one or more &lt;code>imagePullSecrets&lt;/code>, and any pod that binds the ServiceAccount will automatically include any pull secrets provided there. By adding the imagePullSecret to a ServiceAccount, we can streamline including it everywhere that it is needed.&lt;/p>
&lt;p>We can apply a list of transformations with &lt;code>kustomize.applyList&lt;/code> that provides a list of pass-through mutating functions for Jsonnet to apply to each Jsonnet object; in our case we use the &lt;code>updateConfig&lt;/code> function to patch each ServiceAccount with the ImagePullSecret that we want it to use.&lt;/p>
&lt;p>Finally, for staging, we additionally apply &lt;code>kustomize.namespace&lt;/code> to update all resources to use the &lt;code>stg&lt;/code> namespace instead of the &lt;code>prod&lt;/code> namespace. The secret can be copied anywhere we want within the reach of our Flux Kustomization, and since our Flux Kustomization still has &lt;code>cluster-admin&lt;/code> and local access to the decryption key, there is no obstacle to copying secrets.&lt;/p>
&lt;h4 id="handling-secrets">Handling &lt;code>Secret&lt;/code>s&lt;/h4>
&lt;p>Because a &lt;code>secret&lt;/code> is not safe to store in Git unencrypted, Flux recommends using SOPS to encrypt it.&lt;/p>
&lt;p>SOPS will produce a
&lt;a href="https://github.com/mozilla/sops/issues/315" target="_blank">different data key&lt;/a> for each fresh invocation of &lt;code>sops -e&lt;/code>, producing different cipher data even for the same input data. This is true even when the secret content has not changed. This means, unfortunately, it is not practical for a Manifest Generation routine to implement secret transparency without also granting the capability to read secrets to the CI infrastructure.&lt;/p>
&lt;p>SOPS stores the metadata required to decrypt each secret in the metadata of the secret, which must be stored unencrypted to allow encrypted secrets to be read by the private key owners.&lt;/p>
&lt;p>Secret transparency means that it should be possible for an observer to know when a stored secret has been updated or rotated. Transparency can be achieved in SOPS by running &lt;code>sops&lt;/code> as an editor, using &lt;code>sops [encrypted.yaml]&lt;/code>, which decrypts for editing and re-encrypts the secret upon closing the editor, thereby only changing the cipher text when secret data also changes.&lt;/p>
&lt;p>Depending on your access model, this suggestion could be either a complete non-starter, or a helpful add-on.&lt;/p>
&lt;p>As an example, Secrets could be read from GitHub Secrets during a CI job, then written encrypted into a secret that is pushed to the deploy branch. This implementation provides a basic solution for simple centralized secrets rotation. But as this would go way beyond simple manifest generation, we consider this beyond the scope of the tutorial, and it is mentioned only as an example of a more complex usage scenario for users to consider.&lt;/p>
&lt;h4 id="replicate-secrets-across-namespaces">Replicate &lt;code>Secrets&lt;/code> Across Namespaces&lt;/h4>
&lt;p>When the data of a &lt;code>secret&lt;/code> is stored in the Git repository, it can be encrypted to store and transmit safely. SOPS in Kustomize supports encryption of only &lt;code>(stringData|data)&lt;/code> fields, not secret &lt;code>metadata&lt;/code> including &lt;code>namespace&lt;/code>. This means that secrets within the same repo can be copied freely and decrypted somewhere else, just as long as the &lt;code>Kustomization&lt;/code> still has access to the SOPS private key.&lt;/p>
&lt;p>Because of these properties though, copying a SOPS-encrypted secret from one namespace to another within one single Flux tenant is as easy as cloning the YAML manifest and updating the &lt;code>namespace&lt;/code> field. Compared to SealedSecrets controller, which does not permit this type of copying; SOPS, on the other hand, does not currently prevent this without some attention being paid to RBAC.&lt;/p>
&lt;p>Remember to protect your secrets with RBAC! This is not optional, when handling secrets as in this example.&lt;/p>
&lt;h4 id="protecting-secrets-from-unauthorized-access">Protecting &lt;code>Secrets&lt;/code> from Unauthorized Access&lt;/h4>
&lt;p>The logical boundary of a secret is any cluster or tenant where the private key is available for decrypting.&lt;/p>
&lt;p>This means that any SOPS secret, once encrypted, can be copied anywhere or used as a base for other Kustomizations in the cluster, so long as the Kustomization itself has access to the decryption keys.&lt;/p>
&lt;p>It is important to understand that the &lt;code>sops-gpg&lt;/code> key that is generated in the Flux SOPS guide can be used by any &lt;code>Kustomization&lt;/code> in the &lt;code>flux-system&lt;/code> namespace.&lt;/p>
&lt;p>It cannot be over-emphasized; if users want secrets to remain secret, the &lt;code>flux-system&lt;/code> namespace (and indeed the entire cluster itself) must be hardened and protected, managed by qualified cluster admins. It is recommended that changes which could access encrypted secrets are tightly controlled as much as deemed appropriate.&lt;/p>
&lt;h4 id="more-advanced-secrets-usage">More Advanced Secrets Usage&lt;/h4>
&lt;p>The use of KMS as opposed to in-cluster GPG keys with SOPS is left as an exercise for the reader. The basics of KMS with various cloud providers is covered in more depth by the
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/mozilla-sops/#encrypting-secrets-using-various-cloud-providers">Mozilla SOPS&lt;/a> guide.&lt;/p>
&lt;p>Another scenario we considered, but rejected for these examples, requires to decrypt and then re-encrypt SOPS secrets, for use with the &lt;code>secretGenerator&lt;/code> feature of Kustomize. This workflow is not supported here for reasons already explained.&lt;/p>
&lt;p>Flux suggests maintaining the only active copy of the decryption key for a cluster inside of that cluster (though there may be a provision for backups, or some alternate keys permitted to decrypt.) This arrangement makes such use cases significantly more complicated to explain, beyond the scope of this guide.&lt;/p>
&lt;p>For those uses though, additional Workflow Actions are provided:&lt;/p>
&lt;p>The
&lt;a href="https://github.com/marketplace/actions/decrypt-sops-secrets" target="_blank">Decrypt SOPS Secrets&lt;/a> action may be useful and it is mentioned here, (but no example uses are provided.)&lt;/p>
&lt;p>The
&lt;a href="https://github.com/marketplace/actions/sops-binary-installer" target="_blank">Sops Binary Installer&lt;/a> action enables more advanced use cases, like encrypting or re-encrypting secrets.&lt;/p>
&lt;h4 id="jsonnet-recap">Jsonnet Recap&lt;/h4>
&lt;p>While much of this type of manipulation could be handled in &lt;code>Kustomization&lt;/code>&amp;rsquo;s &lt;code>postBuild&lt;/code>, via &lt;code>envsubst&lt;/code>, some configurations are more complicated this way. They can be better handled in CI, where access to additional tools can be provided.&lt;/p>
&lt;p>By writing YAML manifests into a Git commit, the same manifests that &lt;code>Kustomize&lt;/code> directly applies, they can be saved for posterity. Or projected out into a new pull request where they can be reviewed before application, or with the proper safe-guards in place they can be applied immediately through a more direct-driven automation.&lt;/p>
&lt;p>With generated YAML that Flux applies in the cluster directly from Git commits, &lt;strong>fui-yoh&lt;/strong> - that&amp;rsquo;s GitOps!&lt;/p>
&lt;h3 id="commit-across-repositories-workflow">Commit Across Repositories Workflow&lt;/h3>
&lt;p>Flux will not deploy from pushes on just any branch; GitRepository sources target just one specific branch. Merging to a &lt;code>staging&lt;/code> branch, for example, can be used to trigger a deployment to a Staging environment.&lt;/p>
&lt;p>Manifest generation can be used to solve, broadly, very many problems, such that even with many examples, this guide would never be totally exhaustive.&lt;/p>
&lt;p>This is the final example in this guide.&lt;/p>
&lt;p>Here we show 🥁 &amp;hellip; how to replicate the original behavior of Flux v1&amp;rsquo;s image automation! 🤯 🎉&lt;/p>
&lt;p>You can put this workflow in your application repo, and target it toward your &lt;code>fleet-infra&lt;/code> repo.&lt;/p>
&lt;p>To replicate the nearest approximation of Flux&amp;rsquo;s &amp;ldquo;deploy latest image&amp;rdquo; feature of yesteryore, we use push events to do the job, as we hinted was possible in an earlier example. This can be done without Flux v1&amp;rsquo;s redundant and expensive image pull behavior, retrieving build metadata required to order image tags for deployment.&lt;/p>
&lt;p>Flux recommends using real version numbers in your image tags, with a canonical ordering.&lt;/p>
&lt;p>The alternative is racy and doesn&amp;rsquo;t always guarantee the latest commit will be the one that is deployed, since this behavior depends on the time that each commit is pushed, and even precisely how long the build takes to complete; the difference is fine for Dev environments, but this is not a strategy for Production use cases.&lt;/p>
&lt;p>Your app&amp;rsquo;s CI can commit and push YAML manifests (or one manifest for each app) into a separate deploy branch for &lt;code>Kustomization&lt;/code> to apply. The deploy branch in a separate repository should be a branch to which the CI user is granted write access.&lt;/p>
&lt;p>While there are some issues, this is actually perfect for non-prod deployments, eg. in a test environment!&lt;/p>
&lt;p>In context, find
&lt;a href="https://github.com/kingdonb/any_old_app/blob/main/.github/workflows/04-update-fleet-infra.yaml" target="_blank">04-update-fleet-infra.yaml&lt;/a>, or simply copy it from below.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># ./.github/workflows/04-update-fleet-infra.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Update Fleet-Infra&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">on&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">push&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">branches&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#4070a0">&amp;#39;main&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">jobs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Push Update&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">runs-on&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ubuntu-latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">steps&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Prepare&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>prep&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> VERSION=${GITHUB_SHA::8}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> if [[ $GITHUB_REF == refs/tags/* ]]; then
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> VERSION=${GITHUB_REF/refs\/tags\//}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> fi
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo BUILD_DATE=$(date -u +&amp;#39;%Y-%m-%dT%H:%M:%SZ&amp;#39;) &amp;gt;&amp;gt; $GITHUB_OUTPUT
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> echo VERSION=${VERSION} &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Checkout repo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>actions/checkout@v3&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Update manifests&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>./update-k8s.sh $GITHUB_SHA&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Push directory to another repository&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>cpina/github-action-push-to-another-repository@v1.2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">API_TOKEN_GITHUB&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.API_TOKEN_GITHUB }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">source-directory&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#39;flux-config&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination-github-username&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#39;kingdonb&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination-repository-name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#39;fleet-infra&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">target-branch&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#39;deploy&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">user-email&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kingdon+bot@weave.works&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">commit-message&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;[ci skip] deploy from ${{ steps.prep.outputs.VERSION }}&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is
&lt;a href="https://github.com/marketplace/actions/push-directory-to-another-repository" target="_blank">Push directory to another repository&lt;/a>. This is especially useful because Flux v2 is made to work with more than one GitRepository.&lt;/p>
&lt;p>If you must use a mono-repo, consider adding a deploy branch to it! There is no need for branches in the same repo to always share a parent and intersect again at a merge point.&lt;/p>
&lt;p>A mono-repo can be counter-productive for performance and will create bottlenecks for Flux, as large commits will take longer to clone, and therefore to reconcile. Ignoring with &lt;code>.sourceignore&lt;/code> or &lt;code>spec.ignore&lt;/code> will unfortunately not help much with this. Some limitations can only be overcome by changing the data structure.&lt;/p>
&lt;p>The &lt;code>flux-system&lt;/code> is in the &lt;code>main&lt;/code> branch of &lt;code>kingdonb/fleet-infra&lt;/code>, as is the default. We prepared in advance, an empty commit with no parent in the same repository, on the &lt;code>deploy&lt;/code> branch, so that this checkout would begin with an empty workspace that &lt;code>ci/rake.sh&lt;/code> could copy the &lt;code>output/&lt;/code> of Jsonnet into.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>git checkout --orphan deploy
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git reset --hard
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git commit --allow-empty -m&lt;span style="color:#4070a0">&amp;#39;initial empty commit&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git push origin deploy
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is not technically regressive when compared to the behavior of Flux v1&amp;rsquo;s &lt;code>fluxcd.io/automated&lt;/code>, actually avoiding image pull depending on push instead to write the latest Git tag, externally and functionally identical to how Flux v1 did automation. Little else is good that we can say about it.&lt;/p>
&lt;p>It is a compatibility shim, to bridge the gap for Flux v1 users. If possible, users are encouraged to migrate to using timestamps, build numbers, or semver tags, that are all supported by some
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/image-update/">Flux v2 image automation&lt;/a> features that are still in alpha at the time of this writing.&lt;/p>
&lt;p>Flux&amp;rsquo;s new
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/components/image/">Image Automation Controllers&lt;/a> are the new solution for Production use!&lt;/p>
&lt;h3 id="adapting-for-flux-v2">Adapting for Flux v2&lt;/h3>
&lt;p>In Flux v2, with &lt;code>ImagePolicy&lt;/code>, these examples may be adjusted to order tags by their &lt;code>BUILD_DATE&lt;/code>, by adding more string information to the tags. Besides a build timestamp, we can also add branch name.&lt;/p>
&lt;p>Why not have it all: &lt;code>${branch}-${sha}-${ts}&lt;/code> – this is the suggestion given in:&lt;/p>
&lt;ul>
&lt;li>
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/sortable-image-tags/#example-of-a-build-process-with-timestamp-tagging">Example of a build process with timestamp tagging&lt;/a>.&lt;/li>
&lt;/ul>
&lt;p>Example formats and alternative strings to use for tagging are at:&lt;/p>
&lt;ul>
&lt;li>
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/sortable-image-tags/#formats-and-alternatives">Sortable image tags to use with automation&lt;/a>.&lt;/li>
&lt;/ul>
&lt;p>We don&amp;rsquo;t expect you to follow these examples to the letter. They present an evolution and are meant to show some of the breadth of options that are available, rather than as prescriptive guidance.&lt;/p>
&lt;p>If you are on GitHub, and are struggling to get started using GitHub Actions, or maybe still waiting to make a move on your planned migration from Flux v1; we hope that these GitHub Actions examples can help Flux users to better bridge the gap between both versions.&lt;/p></description></item><item><title>Flux: GitHub Actions Auto Pull Request</title><link>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/gh-actions-auto-pr/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/gh-actions-auto-pr/</guid><description>
&lt;p>This guide shows how to configure GitHub Actions to open a pull request whenever a selected branch is pushed.&lt;/p>
&lt;p>From
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/image-update/">Image Update Guide&lt;/a> we saw that Flux can set &lt;code>.spec.git.push.branch&lt;/code> to
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/image-update/#push-updates-to-a-different-branch">Push updates to a different branch&lt;/a> than the one used for checkout.&lt;/p>
&lt;p>Configure an &lt;code>ImageUpdateAutomation&lt;/code> resource to push to a target branch, where we can imagine some policy dictates that updates must be staged and approved for production before they can be deployed.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ImageUpdateAutomation&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">git&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">checkout&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">ref&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">branch&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>main&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">push&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">branch&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>staging&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>We can show that the automation generates a change in the &lt;code>staging&lt;/code> branch which, once the change is approved and merged, gets deployed into production. The image automation is meant to be gated behind a pull request approval workflow, according to policy you may have in place for your repository.&lt;/p>
&lt;p>To create the pull request whenever automation creates a new branch, in your manifest repository, add a GitHub Action workflow as below. This workflow watches for the creation of the &lt;code>staging&lt;/code> branch and opens a pull request with any desired labels, title text, or pull request body content that you configure.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># ./.github/workflows/staging-auto-pr.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Staging Auto-PR&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">on&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">create&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">jobs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">pull-request&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">runs-on&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ubuntu-latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">if&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> github.event.ref_type == &amp;#39;branch&amp;#39; &amp;amp;&amp;amp;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> github.event.ref == &amp;#39;staging&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">permissions&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">pull-requests&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>write&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">steps&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Checkout&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>actions/checkout@v4&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">fetch-depth&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">0&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Create Pull Request&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">GH_TOKEN&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.GITHUB_TOKEN }}&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># used implicitly by the gh CLI to authenticate with GitHub&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">GITHUB_REPO&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ github.repository }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">GITHUB_REF&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ github.ref }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> gh pr create \
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> --repo=${GITHUB_REPO} \
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> --head=staging \
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> --base=main \
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> --title=&amp;#34;Pulling ${GITHUB_REF} into main&amp;#34; \
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> --body=&amp;#34;:crown: *An automated PR*&amp;#34; \
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> --reviewer=kingdonb \
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> --draft&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In the example above, &lt;code>--head&lt;/code> is the source branch and &lt;code>--base&lt;/code> is the destination branch.&lt;/p>
&lt;p>You can use th workflow above to automatically open a pull request against a destination branch. When &lt;code>staging&lt;/code> is merged into the &lt;code>main&lt;/code> branch, changes are deployed in production. Be sure to delete the branch after merging so that the workflow runs the next time that the image automation finds something to change, for that you can go to your
repository settings and enable the &lt;code>Automatically delete head branches&lt;/code> option.&lt;/p>
&lt;div class="alert alert-primary" role="alert">
&lt;h4 class="alert-heading">Additional options&lt;/h4>
The &lt;code>gh pr create&lt;/code> CLI command used in the workflow above has more useful options, like &lt;code>--fill-first&lt;/code>, &lt;code>--label&lt;/code> and &lt;code>--assignee&lt;/code>, that setting will help make this workflow more usable. You can assign reviewers, labels, (use markdown emojis in the &lt;code>--body&lt;/code>, make variable substitutions in the title, etc.)
&lt;/div>
&lt;p>This way you can automatically push changes to a &lt;code>staging&lt;/code> branch and require review with manual approval of any automatic image updates, before they are applied on your production clusters.&lt;/p>
&lt;p>Experiment with these strategies to find the right automated workflow solution for your team!&lt;/p></description></item><item><title>Flux: Jenkins + Flux</title><link>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/jenkins/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2413--fluxcd.netlify.app/flux/use-cases/jenkins/</guid><description>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Disclaimer&lt;/h4>
&lt;p>Note that this guide has not been updated since more than a year ago, it does not address Kubernetes 1.24 or above, and needs to be refreshed.&lt;/p>
&lt;p>Expect this doc to either be archived soon, or to receive an overhaul.&lt;/p>
&lt;/div>
&lt;p>This guide explains how to configure Flux with Jenkins, with the core ideas of
&lt;a href="https://www.gitops.tech/#how-does-gitops-work" target="_blank">GitOps Principles&lt;/a> in mind. Let Jenkins handle CI (or Continuous Integration: image build and test, tagging and pushing), and let Flux handle CD (or Continuous Deployment) by making use of the Image Update Automation feature.&lt;/p>
&lt;h2 id="declarative-artifacts">Declarative Artifacts&lt;/h2>
&lt;p>In traditional CI/CD systems like Jenkins, the CI infra is often made directly responsible for continuous delivery. Flux treats this arrangement as dangerous, and mitigates this risk by prescribing encapsulation of resources into declarative artifacts, and a strict boundary separating CI from CD.&lt;/p>
&lt;p>Jenkins (or any CI system generally speaking) can be cumbersome, and CI builds are an imperative operation that can succeed or fail including sometimes for surprising reasons. Flux obviates any deploy-time link between Jenkins and production deployments, to fulfill the promises of increased reliability and repeatability with GitOps.&lt;/p>
&lt;h3 id="git-manifests">Git Manifests&lt;/h3>
&lt;p>Flux requires YAML manifests to be kept in a git repository or similar artifact hosting, (such as bucket storage which can also be configured to maintain a revision history.) Each revision represents a new &amp;ldquo;desired state&amp;rdquo; for the cluster workloads.&lt;/p>
&lt;p>Flux represents this as a
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/components/source/gitrepositories/">&lt;code>GitRepository&lt;/code> custom resource&lt;/a>.&lt;/p>
&lt;p>Flux&amp;rsquo;s Source Controller periodically reconciles the config repository where the cluster&amp;rsquo;s YAML manifests are maintained, pulling the latest version of the named ref. To invoke the continuous deployment functionality we use a &lt;code>ref&lt;/code> which may be updated, such as a branch or tag. Flux deploys updates from whatever commit is at the ref, every &lt;code>spec.interval&lt;/code>.&lt;/p>
&lt;p>A revision can be any commit at the head of the branch or tag, or a specific commit hash described in the field &lt;code>spec.ref&lt;/code> in the &lt;code>GitRepository&lt;/code>. We could also specify a semver expression here, so that Flux infers the latest tag within a range specified. There are many possible configurations.&lt;/p>
&lt;p>A specific commit hash can also be listed in the &lt;code>GitRepositoryRef&lt;/code>, though this is less common than pointing to a branch, as in this case Flux is no longer performing continuous delivery of changes, but instead reconciles with some specific fixed revision as listed and curated by hand. This behavior, though not particularly ergonomic, could be useful during an
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/image-update/#incident-management">incident response&lt;/a>.&lt;/p>
&lt;p>Pinning to a particular revision allows an operator to handle updates explicitly and manually but still via GitOps. While this effectively disables some automated features of Flux, it is also a capable way to operate. Jenkins jobs could also write commit hashes into manifests, and while cats can live with dogs, this is not important for this example.&lt;/p>
&lt;p>When adopting Flux&amp;rsquo;s approach to GitOps, Jenkins should not &lt;code>kubectl apply&lt;/code> your manifests anymore. Jenkins does not need direct access to any production clusters. The responsibilities of Jenkins are reduced from a traditional CI/CD system which does both, to only CI. The CD job is delegated entirely to Flux. From the perspective of a production cluster, Jenkins&amp;rsquo; responsibility has ended once an image artifact is built and tested, after it has been tagged for deployment and pushed to the Image Repository.&lt;/p>
&lt;h3 id="image-repository">Image Repository&lt;/h3>
&lt;p>Jenkins is responsible for building and testing OCI images. Those images can be tested internally on Jenkins as part of the build, before pushing, or once deployed in a non-production cluster or other staging context.&lt;/p>
&lt;p>Jenkins workflows are often as varied and complex as a snowflake. Finer points of Jenkins image building are not in scope for this guide, but links and some examples are provided to show a strategy that can be used on Jenkins with Flux.&lt;/p>
&lt;p>If you&amp;rsquo;re getting started and maybe have not much experience with Jenkins, (or if perhaps it was the boss who said to use Jenkins,) we hope this information comes in handy.&lt;/p>
&lt;h4 id="jenkins-builds-oci-images">Jenkins Builds OCI Images&lt;/h4>
&lt;p>While many companies and people may use Jenkins for building Docker images, it isn&amp;rsquo;t actually true that Jenkins builds Docker images.&lt;/p>
&lt;p>Docker builds OCI images, and Jenkins can shell out to Docker to build images.&lt;/p>
&lt;p>Jenkins always uses some other tool to build OCI images. Docker may still be the most commonly reported such tool in use as of this writing, but other tools are growing in popularity like
&lt;a href="https://porter.sh" target="_blank">Porter&lt;/a>,
&lt;a href="https://buildpacks.io" target="_blank">Buildpacks.io&lt;/a>,
&lt;a href="https://earthly.dev" target="_blank">Earthfile&lt;/a>, and many others we have not seen, many which could be used with Jenkins.&lt;/p>
&lt;p>One might use
&lt;a href="https://www.jenkins.io/doc/book/pipeline/docker/" target="_blank">declarative pipelines&lt;/a> to include the CI configuration in application repos by writing a Jenkinsfile to suit your own needs. We might use the
&lt;a href="https://plugins.jenkins.io/docker-plugin/" target="_blank">docker plugin&lt;/a>, or a privileged pod with HostPath volume to mount &lt;code>/var/run/docker.sock&lt;/code>. There are many strategies; this example only shows one way.&lt;/p>
&lt;h4 id="security-concerns">Security Concerns&lt;/h4>
&lt;p>This should attract the attention of InfoSec advocates, as privileged pods and HostPath volumes are generally acknowledged as &lt;strong>extremely dangerous&lt;/strong>.&lt;/p>
&lt;p>You should not ever do these things without understanding the risks. Don&amp;rsquo;t run untrusted code from an unverified source, anywhere near a production cluster. (OK, that&amp;rsquo;s fine, and we already acknowledged that Jenkins-CI is to be completely separated from production, according to Flux&amp;hellip;)&lt;/p>
&lt;h4 id="dockershim-was-formally-deprecated">Dockershim was formally deprecated&lt;/h4>
&lt;p>We might also be running Jenkins on a Kubernetes cluster that
&lt;a href="https://kubernetes.io/blog/2020/12/02/dockershim-faq/#why-is-dockershim-being-deprecated" target="_blank">doesn&amp;rsquo;t even have Docker underneath&lt;/a>. In this case you may want to use another tool to produce OCI images with Jenkins.&lt;/p>
&lt;p>Here, we will use a privileged pod with access to Docker on the host to its most positive effect, by building the image on a single node cluster which is specially earmarked and set aside for Jenkins builds.&lt;/p>
&lt;p>This is so that we can leverage Docker&amp;rsquo;s node-local image storage while building and testing images. When the next stage of the pipeline is executed after an image is built, there is no need to push or pull from a remote image registry as we are on the same machine where the image was built.&lt;/p>
&lt;p>Now while this strategy is efficient, it will only work if Kubernetes is running Docker underneath (and that is certainly getting less common now, though it should fortunately remain possible into the future now that
&lt;a href="https://www.mirantis.com/blog/mirantis-to-take-over-support-of-kubernetes-dockershim-2/" target="_blank">Mirantis has taken over support of dockershim&lt;/a>.)&lt;/p>
&lt;h5 id="ci-is-out-of-scope-for-flux">CI is out of scope for Flux&lt;/h5>
&lt;p>Jenkins users will have to find a way of making sense of all this, depending on the details of your situation. None of this is reasonably in scope for Flux to solve, as Flux separates the responsibilities of CI and CD across these well-defined boundaries.&lt;/p>
&lt;p>Whatever tool you use for building images, this document aims to explain and show compatible choices that work well with Flux.&lt;/p>
&lt;p>It should be clear that if we use Jenkins or Docker, or any other competing tool for building images, much of the same advice for working with Flux will still apply.&lt;/p>
&lt;h4 id="what-should-we-do">What Should We Do?&lt;/h4>
&lt;p>We recommend users implement SemVer and/or
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/sortable-image-tags/">Sortable image tags&lt;/a>, to enable the use of
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/image-update/">Image Update Policies&lt;/a>. It is possible for Jenkins to drive deployments with Flux in this way securely through tags, without direct access or explicit coordination with production environment or staging clusters.&lt;/p>
&lt;p>GitOps principles suggest that we should manage production workloads as purely declarative artifacts that accurately describe the cluster state in enough detail to reproduce, including version information. Extrapolating the principles, we can also prescribe updates to container images with an automated process, and appropriately constrain this process to only new SemVer releases within a specified range.&lt;/p>
&lt;h4 id="documentation-references">Documentation References&lt;/h4>
&lt;p>Many parts are needed for a complete continuous delivery pipeline with Jenkins and Flux.&lt;/p>
&lt;p>The reference below shows how to build and tag Docker images with Jenkins for development environments, for executing tests in a pipeline stage, and lastly tagging a release version for deployment in a production setting.&lt;/p>
&lt;h5 id="testing-infrastructure">Testing Infrastructure&lt;/h5>
&lt;p>We can do testing without an image registry or pushing or pulling any image, because Jenkins builds the image locally on the build node, with a privileged pod that has direct access to Docker on the host. This is significantly faster than pushing before testing.&lt;/p>
&lt;p>So (if we&amp;rsquo;re using a single-node cluster for Jenkins, or by some other tricks perhaps &amp;hellip;) the image is created and used with the single node&amp;rsquo;s Docker daemon.&lt;/p>
&lt;p>Those are some assumptions that you may need to check on&amp;hellip; anyway, then we tag and push an image any time a new release version was tagged in Git, only after seeing the tests pass.&lt;/p>
&lt;h5 id="jenkinscipipeline-examples-repo">&lt;code>jenkinsci/pipeline-examples&lt;/code> repo&lt;/h5>
&lt;p>Jenkins provides examples of declarative pipelines that use
&lt;a href="https://github.com/jenkinsci/pipeline-examples/blob/master/declarative-examples/simple-examples/credentialsUsernamePassword.groovy" target="_blank">credentials&lt;/a> and show how you can use string data elements collected or composed in earlier stages, to drive downstream stages or scripts in different ways, or simply
&lt;a href="https://github.com/jenkinsci/pipeline-examples/blob/master/declarative-examples/simple-examples/scriptVariableAssignment.groovy" target="_blank">populating environment variables&lt;/a>.&lt;/p>
&lt;p>Another example executes certain workflow scripts
&lt;a href="https://github.com/jenkinsci/pipeline-examples/blob/master/declarative-examples/simple-examples/whenBranchMaster.groovy" target="_blank">only against a particular branch&lt;/a>. We may need to do all of these things, or similar ideas, in order to test and release new versions of our app in production.&lt;/p>
&lt;p>For more information on Jenkins pipelines, visit the
&lt;a href="https://github.com/jenkinsci/pipeline-examples/tree/master/declarative-examples" target="_blank">jenkinsci/pipeline-examples&lt;/a> declarative examples.&lt;/p>
&lt;h2 id="example-jenkinsfile">Example Jenkinsfile&lt;/h2>
&lt;p>Adapt this if needed, or add this to a project repository with a &lt;code>Dockerfile&lt;/code> in its root, as a file &lt;code>Jenkinsfile&lt;/code>, and configure a
&lt;a href="https://www.jenkins.io/doc/book/pipeline/multibranch/#creating-a-multibranch-pipeline" target="_blank">Multibranch Pipeline&lt;/a> to trigger when new commits are pushed to any branch or tag.&lt;/p>
&lt;p>Find this example in context at
&lt;a href="https://github.com/kingdonb/jenkins-example-workflow" target="_blank">kingdonb/jenkins-example-workflow&lt;/a> where it is connected with a Jenkins server, and configured to build and push images to
&lt;a href="https://hub.docker.com/r/kingdonb/jenkins-example-workflow/tags?page=1&amp;amp;ordering=last_updated" target="_blank">docker.io/kingdonb/jenkins-example-workflow&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-groovy" data-lang="groovy">&lt;span style="display:flex;">&lt;span>dockerRepoHost &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;docker.io&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dockerRepoUser &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;kingdonb&amp;#39;&lt;/span> &lt;span style="color:#60a0b0;font-style:italic">// (Username must match the value in jenkinsDockerSecret)
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>dockerRepoProj &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;jenkins-example-workflow&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// these refer to a Jenkins secret &amp;#34;id&amp;#34;, which can be in Jenkins global scope:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>jenkinsDockerSecret &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;docker-registry-account&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">// blank values that are filled in by pipeline steps below:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span>gitCommit &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>branchName &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>unixTime &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>developmentTag &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>releaseTag &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>pipeline &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> agent &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kubernetes &lt;span style="color:#666">{&lt;/span> yamlFile &lt;span style="color:#4070a0">&amp;#34;jenkins/docker-pod.yaml&amp;#34;&lt;/span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> stages &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// Build a Docker image and keep it locally for now
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> stage&lt;span style="color:#666">(&lt;/span>&lt;span style="color:#4070a0">&amp;#39;Build&amp;#39;&lt;/span>&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> steps &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> container&lt;span style="color:#666">(&lt;/span>&lt;span style="color:#4070a0">&amp;#39;docker&amp;#39;&lt;/span>&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> script &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> gitCommit &lt;span style="color:#666">=&lt;/span> env&lt;span style="color:#666">.&lt;/span>&lt;span style="color:#4070a0">GIT_COMMIT&lt;/span>&lt;span style="color:#666">.&lt;/span>&lt;span style="color:#4070a0">substring&lt;/span>&lt;span style="color:#666">(&lt;/span>&lt;span style="color:#40a070">0&lt;/span>&lt;span style="color:#666">,&lt;/span>&lt;span style="color:#40a070">8&lt;/span>&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> branchName &lt;span style="color:#666">=&lt;/span> env&lt;span style="color:#666">.&lt;/span>&lt;span style="color:#4070a0">BRANCH_NAME&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> unixTime &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#666">(&lt;/span>&lt;span style="color:#007020;font-weight:bold">new&lt;/span> Date&lt;span style="color:#666">().&lt;/span>&lt;span style="color:#4070a0">time&lt;/span>&lt;span style="color:#666">.&lt;/span>&lt;span style="color:#4070a0">intdiv&lt;/span>&lt;span style="color:#666">(&lt;/span>&lt;span style="color:#40a070">1000&lt;/span>&lt;span style="color:#666">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> developmentTag &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#34;${branchName}-${gitCommit}-${unixTime}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> developmentImage &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#34;${dockerRepoUser}/${dockerRepoProj}:${developmentTag}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sh &lt;span style="color:#4070a0">&amp;#34;docker build -t ${developmentImage} ./&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// Push the image to development environment, and run tests in parallel
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> stage&lt;span style="color:#666">(&lt;/span>&lt;span style="color:#4070a0">&amp;#39;Dev&amp;#39;&lt;/span>&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> parallel &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> stage&lt;span style="color:#666">(&lt;/span>&lt;span style="color:#4070a0">&amp;#39;Push Development Tag&amp;#39;&lt;/span>&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> when &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> not &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> buildingTag&lt;span style="color:#666">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> steps &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> withCredentials&lt;span style="color:#666">([[&lt;/span>$class&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;UsernamePasswordMultiBinding&amp;#39;&lt;/span>&lt;span style="color:#666">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#002070;font-weight:bold">credentialsId:&lt;/span> jenkinsDockerSecret&lt;span style="color:#666">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#002070;font-weight:bold">usernameVariable:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;DOCKER_REPO_USER&amp;#39;&lt;/span>&lt;span style="color:#666">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#002070;font-weight:bold">passwordVariable:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;DOCKER_REPO_PASSWORD&amp;#39;&lt;/span>&lt;span style="color:#666">]])&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> container&lt;span style="color:#666">(&lt;/span>&lt;span style="color:#4070a0">&amp;#39;docker&amp;#39;&lt;/span>&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sh &lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> docker login -u \$DOCKER_REPO_USER -p \$DOCKER_REPO_PASSWORD
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> docker push ${developmentImage}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#666">.&lt;/span>&lt;span style="color:#4070a0">stripIndent&lt;/span>&lt;span style="color:#666">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// Start a second agent to create a pod with the newly built image
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> stage&lt;span style="color:#666">(&lt;/span>&lt;span style="color:#4070a0">&amp;#39;Test&amp;#39;&lt;/span>&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> agent &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> kubernetes &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> yaml &lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> apiVersion: v1
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> kind: Pod
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> spec:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> containers:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> - name: test
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> image: ${developmentImage}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> imagePullPolicy: Never
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> securityContext:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> runAsUser: 1000
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> command:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> - cat
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> resources:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> requests:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> memory: 256Mi
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> cpu: 50m
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> limits:
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> memory: 1Gi
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> cpu: 1200m
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> tty: true
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#666">.&lt;/span>&lt;span style="color:#4070a0">stripIndent&lt;/span>&lt;span style="color:#666">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> options &lt;span style="color:#666">{&lt;/span> skipDefaultCheckout&lt;span style="color:#666">(&lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> steps &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#60a0b0;font-style:italic">// Run the tests in the new test container
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic">&lt;/span> container&lt;span style="color:#666">(&lt;/span>&lt;span style="color:#4070a0">&amp;#39;test&amp;#39;&lt;/span>&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sh &lt;span style="color:#666">(&lt;/span>&lt;span style="color:#002070;font-weight:bold">script:&lt;/span> &lt;span style="color:#4070a0">&amp;#34;/app/jenkins/run-tests.sh&amp;#34;&lt;/span>&lt;span style="color:#666">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> stage&lt;span style="color:#666">(&lt;/span>&lt;span style="color:#4070a0">&amp;#39;Push Release Tag&amp;#39;&lt;/span>&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> when &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> buildingTag&lt;span style="color:#666">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> steps &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> script &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> releaseTag &lt;span style="color:#666">=&lt;/span> env&lt;span style="color:#666">.&lt;/span>&lt;span style="color:#4070a0">TAG_NAME&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> releaseImage &lt;span style="color:#666">=&lt;/span> &lt;span style="color:#4070a0">&amp;#34;${dockerRepoUser}/${dockerRepoProj}:${releaseTag}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> container&lt;span style="color:#666">(&lt;/span>&lt;span style="color:#4070a0">&amp;#39;docker&amp;#39;&lt;/span>&lt;span style="color:#666">)&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> withCredentials&lt;span style="color:#666">([[&lt;/span>$class&lt;span style="color:#666">:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;UsernamePasswordMultiBinding&amp;#39;&lt;/span>&lt;span style="color:#666">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#002070;font-weight:bold">credentialsId:&lt;/span> jenkinsDockerSecret&lt;span style="color:#666">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#002070;font-weight:bold">usernameVariable:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;DOCKER_REPO_USER&amp;#39;&lt;/span>&lt;span style="color:#666">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#002070;font-weight:bold">passwordVariable:&lt;/span> &lt;span style="color:#4070a0">&amp;#39;DOCKER_REPO_PASSWORD&amp;#39;&lt;/span>&lt;span style="color:#666">]])&lt;/span> &lt;span style="color:#666">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sh &lt;span style="color:#4070a0">&amp;#34;&amp;#34;&amp;#34;\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> docker login -u \$DOCKER_REPO_USER -p \$DOCKER_REPO_PASSWORD
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> docker tag ${developmentImage} ${releaseImage}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> docker push ${releaseImage}
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#666">.&lt;/span>&lt;span style="color:#4070a0">stripIndent&lt;/span>&lt;span style="color:#666">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#666">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The example above should do the necessary work for building, tagging, and pushing images for development and production.&lt;/p>
&lt;h3 id="instructions-for-use">Instructions for Use&lt;/h3>
&lt;p>Fork the repo from
&lt;a href="https://github.com/kingdonb/jenkins-example-workflow" target="_blank">kingdonb/jenkins-example-workflow&lt;/a>, to see the other details like a basic suitable example
&lt;a href="https://github.com/kingdonb/jenkins-example-workflow/blob/main/Dockerfile" target="_blank">Dockerfile&lt;/a>, the
&lt;a href="https://github.com/kingdonb/jenkins-example-workflow/blob/main/jenkins/docker-pod.yaml" target="_blank">jenkins/docker-pod.yaml&lt;/a>, and the
&lt;a href="https://github.com/kingdonb/jenkins-example-workflow/blob/main/jenkins/run-tests.sh" target="_blank">jenkins/run-tests.sh&lt;/a> script.&lt;/p>
&lt;p>Create a Multibranch Pipeline and associate it with your repository. Configure it to build commits from at least one branch (or all branches) and any tags.&lt;/p>
&lt;h4 id="development-image">Development Image&lt;/h4>
&lt;p>When you push a commit to a branch, it will be built and tested locally on the Jenkins node, and also a dev image is pushed in parallel to the image repository.&lt;/p>
&lt;p>This image is tagged with a
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/sortable-image-tags/">Sortable image tag&lt;/a> of the format &lt;code>{branch}-{sha}-{ts}&lt;/code>.&lt;/p>
&lt;p>This can be deployed automatically by Flux, with a more relaxed policy for development environments. There is no requirement that tests must pass in order to deploy the latest image in development. You can add such a requirement later, or change this in any way that makes sense for your use case.&lt;/p>
&lt;h4 id="release-semver-image">Release &lt;code>SemVer&lt;/code> Image&lt;/h4>
&lt;p>We will confirm the tests are passing before pushing any production tag, in the &lt;code>Push Release Tag&lt;/code> stage, which only runs after our &lt;code>Test&lt;/code> stage has succeeded.&lt;/p>
&lt;p>The corresponding Flux resource is covered in the
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/sortable-image-tags/#using-in-an-imagepolicy-object">using an ImagePolicy object&lt;/a> section of the aforementioned guide.&lt;/p>
&lt;p>When you push a git tag, different workflow is used because of:&lt;/p>
&lt;pre tabindex="0">&lt;code>when {
buildingTag()
}
&lt;/code>&lt;/pre>&lt;p>and&lt;/p>
&lt;pre tabindex="0">&lt;code>when {
not {
buildingTag()
}
}
&lt;/code>&lt;/pre>&lt;p>This is important since git tags can be used for
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/image-update/">Automating image updates to Git&lt;/a> in production.&lt;/p>
&lt;p>Using SemVer tags, you can automatically promote new tags to production via policy.&lt;/p>
&lt;p>In this guide we assume you will manually create and push Git tags whenever needed, then promote them through the pipeline. These builds will run &lt;code>docker build&lt;/code> again for the tag, which should hit the cache and complete quickly, then run tests again, and finally on success push a SemVer image tag.&lt;/p>
&lt;h4 id="example-resources">Example Resources&lt;/h4>
&lt;p>You can find this example tested with a Jenkins instance at
&lt;a href="https://github.com/kingdonb/jenkins-example-workflow" target="_blank">kingdonb/jenkins-example-workflow&lt;/a>; it has been configured to run as you can see from the commit status checks found on all commits, tags, and pull requests. The images are pushed to
&lt;a href="https://hub.docker.com/r/kingdonb/jenkins-example-workflow/tags?page=1&amp;amp;ordering=last_updated" target="_blank">docker.io/kingdonb/jenkins-example-workflow&lt;/a>.&lt;/p>
&lt;p>Configuration of webhooks would be a logical next step if not already configured and working, so that Jenkins can trigger builds without polling or manual intervention by an operator. Demonstrating this advanced CI feature of Jenkins is again something that is out of scope for this guide.&lt;/p>
&lt;h4 id="wrap-up">Wrap Up&lt;/h4>
&lt;p>By pushing image tags, Jenkins can update the cluster using Flux&amp;rsquo;s pull-based model for updating. When a new image is pushed that has a newer image tag, and meets the filters of the configured policy, Flux is made aware of it by Image Reflector API, which captures the new candidate tag as an Image resource.&lt;/p>
&lt;p>Your CI workflow can be based on these examples, or may turn out completely different. Jenkins has a rich ecosystem of plugins, and the Jenkinsfile is as diverse and powerful as any programming language. If you are using Jenkins already, you probably already know exactly how you want it to build images.&lt;/p>
&lt;p>If you are concerned about running Docker and Kubernetes together, or if you need to use these workflows in a cluster that cannot run container images as root, or in privileged mode, for an alternative build strategy that can still work with Jenkins in rootless mode, we recommend you check out
&lt;a href="https://github.com/moby/buildkit/tree/master/examples/kubernetes" target="_blank">Kubernetes examples for Buildkit&lt;/a> and the
&lt;a href="https://github.com/vmware-tanzu/buildkit-cli-for-kubectl" target="_blank">Buildkit CLI for Kubectl&lt;/a>.&lt;/p>
&lt;p>These were recently presented together at
&lt;a href="https://www.youtube.com/watch?v=vTh6jkW_xtI" target="_blank">KubeCon/CloudNativeCon EU 2021&lt;/a>.&lt;/p>
&lt;p>The finer points of building OCI images in Jenkins are out of scope for this guide. These examples are meant to be kept simple, though complete, and we refrain from sharing strong opinions about how CI should work here, because it&amp;rsquo;s simply out of scope for Flux to weigh in about these topics. We meant to show some ways that Jenkins CI, or any similar functioning tool, can be used with Flux.&lt;/p>
&lt;p>This works without Jenkins connecting to production clusters, only building images, and Flux only receives published image tags. So there is no strong coupling or inter-dependency between CI and CD!&lt;/p>
&lt;p>Update deployments via Flux&amp;rsquo;s
&lt;a href="https://deploy-preview-2413--fluxcd.netlify.app/flux/guides/sortable-image-tags/#using-in-an-imagepolicy-object">ImagePolicy&lt;/a> CRD, and the Image Update Automation API.&lt;/p></description></item></channel></rss>