<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Github-Actions on Backend Engineering Strategy Tools</title><link>https://backend-engineering-strategy-tools.github.io/site/tags/github-actions/</link><description>Recent content in Github-Actions on Backend Engineering Strategy Tools</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Mon, 01 Jan 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://backend-engineering-strategy-tools.github.io/site/tags/github-actions/index.xml" rel="self" type="application/rss+xml"/><item><title>CI/CD Platforms</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/platforms/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/platforms/</guid><description>&lt;p&gt;There are many CI/CD platforms and the choice between them matters less than it appears. All of them are thin orchestration wrappers — trigger on a git event, run some steps, report the result. The build logic itself should live in &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/make/" &gt;Make&lt;/a&gt; or &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/dagger/" &gt;Dagger&lt;/a&gt;, not inside the pipeline definition. See &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/thinking/one-command-any-pipeline/" &gt;One Command, Any Pipeline&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="cruisecontrol"&gt;CruiseControl
&lt;/h2&gt;&lt;p&gt;The early pioneer, released in 2001. CruiseControl introduced continuous integration as a practice — polling a source repository, building on every change, sending email on failure. Configuration was XML, the dashboard was a web page, and it ran on a server you managed yourself. Most of the concepts in modern CI trace back here. Largely historical today but worth knowing as the origin point.&lt;/p&gt;
&lt;h2 id="hudson"&gt;Hudson
&lt;/h2&gt;&lt;p&gt;Hudson was Sun Microsystems&amp;rsquo; take on CI — a Java application with a plugin ecosystem that made it far more extensible than CruiseControl. It gained wide adoption in enterprise Java shops during the late 2000s. When Oracle acquired Sun, the project forked. The community fork became Jenkins; the Oracle-maintained branch kept the Hudson name and eventually faded. Hudson is effectively dead.&lt;/p&gt;
&lt;h2 id="jenkins"&gt;Jenkins
&lt;/h2&gt;&lt;p&gt;The fork that won. Jenkins took Hudson&amp;rsquo;s plugin architecture and ran with it — today it has over 1800 plugins covering almost every tool in the ecosystem. A Jenkinsfile defines the pipeline as Groovy DSL and lives in the repository alongside the code. Jenkins is the most widely deployed self-hosted CI server and the default answer in many enterprises. The flip side: it is heavyweight, the Groovy DSL has sharp edges, and complex pipelines are difficult to test outside of Jenkins itself.&lt;/p&gt;
&lt;h2 id="jenkins-x"&gt;Jenkins X
&lt;/h2&gt;&lt;p&gt;Jenkins X is a cloud-native reimagining of Jenkins for Kubernetes. It imposes a strongly opinionated GitOps workflow — pull requests promote through environments, preview environments spin up automatically, everything is driven by Git events. Built on Tekton under the hood. If you want opinionated Kubernetes CI/CD without building the conventions yourself, Jenkins X is one answer. If you want more control over the pipeline structure, raw Tekton gives you the primitives without the opinions.&lt;/p&gt;
&lt;h2 id="tekton"&gt;Tekton
&lt;/h2&gt;&lt;p&gt;Kubernetes-native CI/CD where pipelines, tasks, and triggers are all Kubernetes CRDs — defined in YAML and applied to a cluster, running as pods. No separate CI server to maintain, no external SaaS dependency. CI runs in the same cluster as your workloads using the same RBAC, secrets, and storage primitives. The core primitives are &lt;code&gt;Task&lt;/code&gt; (a sequence of container steps), &lt;code&gt;Pipeline&lt;/code&gt; (an ordered set of tasks), &lt;code&gt;PipelineRun&lt;/code&gt; (an execution), and &lt;code&gt;Trigger&lt;/code&gt; (an event listener that creates runs). The pattern that works well: keep Tasks thin and have them call &lt;code&gt;make &amp;lt;target&amp;gt;&lt;/code&gt; — Tekton handles orchestration, Make handles logic.&lt;/p&gt;
&lt;h2 id="github-actions"&gt;GitHub Actions
&lt;/h2&gt;&lt;p&gt;GitHub&amp;rsquo;s built-in CI/CD, available to any repository on GitHub. Zero infrastructure, zero setup — if your code is on GitHub, Actions is already there. Workflows are YAML files in &lt;code&gt;.github/workflows/&lt;/code&gt;, triggered by git events, running on GitHub-managed runners. A large marketplace of pre-built actions covers most common tasks. The zero-friction default for open source projects and small teams. See the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/github/" &gt;GitHub Actions&lt;/a&gt; note for more detail.&lt;/p&gt;
&lt;h2 id="argo-workflows"&gt;Argo Workflows
&lt;/h2&gt;&lt;p&gt;A Kubernetes-native workflow engine from the Argo project. Where Tekton models CI primitives (Tasks, Pipelines), Argo Workflows is a general-purpose DAG executor — it can run any containerised workload as a directed acyclic graph of steps, with fan-out, fan-in, conditionals, and retry logic. Widely used as the execution layer under other tools (including Dagger on Kubernetes). Pairs well with &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/argo-cd/" &gt;ArgoCD&lt;/a&gt; for a fully Argo-based GitOps stack. See the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/argo-project/" &gt;Argo&lt;/a&gt; note for coverage of the full Argo ecosystem including Rollouts, Events, and Kargo.&lt;/p&gt;
&lt;h2 id="bitbucket-pipelines"&gt;Bitbucket Pipelines
&lt;/h2&gt;&lt;p&gt;Bitbucket&amp;rsquo;s built-in CI/CD, integrated directly with Atlassian&amp;rsquo;s hosting. If your code is already in Bitbucket, Pipelines is the zero-infrastructure option — the same position GitHub Actions occupies for GitHub users. Workflows are YAML, steps run in Docker containers, and Atlassian handles the runner infrastructure. Tightly integrated with Jira for deployment tracking. The right choice when you&amp;rsquo;re already in the Atlassian ecosystem and don&amp;rsquo;t want to introduce a separate CI tool.&lt;/p&gt;
&lt;h2 id="harness"&gt;Harness
&lt;/h2&gt;&lt;p&gt;A commercial platform with a broader scope than most CI/CD tools — it covers CI, CD, feature flags, cloud cost management, and security testing under one roof. Enterprise-focused, with AI-assisted pipeline generation and strong support for policy and governance across large engineering organisations. Harness is the answer when the organisation needs a managed platform with SLAs, support, and audit trails rather than self-hosted infrastructure. Pricing reflects that positioning.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://tekton.dev/docs/" target="_blank" rel="noopener"
 &gt;Tekton documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.jenkins.io/doc/" target="_blank" rel="noopener"
 &gt;Jenkins documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://jenkins-x.io/docs/" target="_blank" rel="noopener"
 &gt;Jenkins X documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://argoproj.github.io/argo-workflows/" target="_blank" rel="noopener"
 &gt;Argo Workflows documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://support.atlassian.com/bitbucket-cloud/docs/bitbucket-pipelines/" target="_blank" rel="noopener"
 &gt;Bitbucket Pipelines documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://developer.harness.io/" target="_blank" rel="noopener"
 &gt;Harness documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>GitHub Actions</title><link>https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/github/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/github/</guid><description>&lt;p&gt;For a small project or proof of concept, the cost of building a CI environment often exceeds the cost of the project itself. Spinning up a Tekton cluster, installing Argo Workflows, or maintaining a Jenkins server is real infrastructure — with real setup time, real maintenance overhead, and real dependencies to keep running.&lt;/p&gt;
&lt;p&gt;GitHub Actions is already there. If your code is on GitHub, you have CI. No extra infrastructure, no additional accounts, a marketplace of pre-built integrations for almost everything you need. For open source projects and small teams it is the pragmatic default: free, integrated, and good enough for the standard build → test → publish pipeline.&lt;/p&gt;
&lt;h2 id="workflow-anatomy"&gt;Workflow anatomy
&lt;/h2&gt;&lt;p&gt;Workflows live in &lt;code&gt;.github/workflows/&lt;/code&gt; as YAML files. A workflow has triggers, jobs, and steps:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Build and publish&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;on&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;push&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;branches&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;main]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;pull_request&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;branches&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;main]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;jobs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;build&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;runs-on&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ubuntu-latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;uses&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;actions/checkout@v4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Build&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;make build&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;make test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Push image&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;if&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;github.ref == &amp;#39;refs/heads/main&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; echo &amp;#34;${{ secrets.DOCKER_HUB_TOKEN }}&amp;#34; | docker login -u ${{ secrets.DOCKER_HUB_USER }} --password-stdin
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; docker push myorg/myimage:latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;on:&lt;/code&gt; defines what triggers the workflow. &lt;code&gt;runs-on:&lt;/code&gt; selects the runner. &lt;code&gt;uses:&lt;/code&gt; pulls a pre-built action from the marketplace. &lt;code&gt;run:&lt;/code&gt; executes shell commands directly.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;if:&lt;/code&gt; condition on the push step is a common pattern: run tests on every pull request, but only publish on merge to &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="secrets"&gt;Secrets
&lt;/h2&gt;&lt;p&gt;GitHub Secrets store credentials without putting them in the repository. Add them under &lt;strong&gt;Settings → Secrets and variables → Actions&lt;/strong&gt;, then reference them in workflows as &lt;code&gt;${{ secrets.MY_SECRET }}&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For pushing to external services — Docker Hub, a package registry, a cloud provider — this is the integration point. The workflow authenticates using the secret, the secret itself never appears in logs or code.&lt;/p&gt;
&lt;h3 id="organisation-secrets"&gt;Organisation secrets
&lt;/h3&gt;&lt;p&gt;Secrets can be set at the organisation level and inherited by all repositories — no per-repo configuration needed. Useful for shared CI credentials reused across many repos.&lt;/p&gt;
&lt;p&gt;Path: github.com/&lt;code&gt;&amp;lt;org&amp;gt;&lt;/code&gt; → Settings → Secrets and variables → Actions → &lt;strong&gt;New organisation secret&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Set &lt;strong&gt;Repository access&lt;/strong&gt; to &amp;ldquo;All repositories&amp;rdquo; or a selected subset once you know which repos need it.&lt;/p&gt;
&lt;p&gt;A worked example — shared image publishing credentials:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;DOCKERHUB_TOKEN # hub.docker.com → Account → Security → Access Tokens
DAGGER_CLOUD_TOKEN # cloud.dagger.io → Organisation → Tokens
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Set once at org level; every &lt;code&gt;image-*&lt;/code&gt; repo inherits them. Workflows reference them identically to repo secrets: &lt;code&gt;${{ secrets.DOCKERHUB_TOKEN }}&lt;/code&gt;. See &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/dagger/" &gt;Dagger&lt;/a&gt; for &lt;code&gt;DAGGER_CLOUD_TOKEN&lt;/code&gt; context.&lt;/p&gt;
&lt;h2 id="environments"&gt;Environments
&lt;/h2&gt;&lt;p&gt;Environments add a protection layer on top of secrets. Define named environments (e.g. &lt;code&gt;staging&lt;/code&gt;, &lt;code&gt;production&lt;/code&gt;) and configure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Required reviewers&lt;/strong&gt; — a human must approve before the job runs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Environment secrets&lt;/strong&gt; — secrets scoped to that environment only, not available to other jobs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wait timer&lt;/strong&gt; — a mandatory delay before deployment proceeds&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A deployment job targets an environment by name:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;jobs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;deploy&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;runs-on&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ubuntu-latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;environment&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;production&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;./deploy.sh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;env&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;API_KEY&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${{ secrets.PROD_API_KEY }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The job pauses for approval before running. Useful for open source projects where the pipeline is public but deployment credentials are not.&lt;/p&gt;
&lt;h2 id="marketplace-actions"&gt;Marketplace actions
&lt;/h2&gt;&lt;p&gt;Most common tasks have a pre-built action. A few worth knowing:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Action&lt;/th&gt;
 &lt;th&gt;What it does&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;actions/checkout&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Clone the repository&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;actions/setup-go&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Install a specific Go version&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;docker/setup-buildx-action&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Enable multi-arch Docker builds&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;docker/build-push-action&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Build and push a container image&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;helm/kind-action&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Spin up a local Kubernetes cluster for tests&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Using marketplace actions trades control for speed. For a POC or small project that&amp;rsquo;s the right trade. For anything critical, pin the action to a specific commit SHA rather than a tag — tags are mutable.&lt;/p&gt;
&lt;h2 id="self-hosted-runners"&gt;Self-hosted runners
&lt;/h2&gt;&lt;p&gt;GitHub-hosted runners (&lt;code&gt;ubuntu-latest&lt;/code&gt;, &lt;code&gt;windows-latest&lt;/code&gt;) cover most cases. Self-hosted runners make sense when you need access to an internal network, specific hardware (a GPU, specialised build machine), or want to avoid per-minute billing at scale.&lt;/p&gt;
&lt;p&gt;At that point you are closer to running a full CI platform, which is where tools like &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/platforms/" &gt;Tekton&lt;/a&gt; or &lt;a class="link" href="https://argoproj.github.io/argo-workflows/" target="_blank" rel="noopener"
 &gt;Argo Workflows&lt;/a&gt; become relevant — they give you the same kind of pipeline orchestration but running entirely on your own infrastructure.&lt;/p&gt;
&lt;h2 id="resources"&gt;Resources
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.github.com/en/actions" target="_blank" rel="noopener"
 &gt;GitHub Actions documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/marketplace?type=actions" target="_blank" rel="noopener"
 &gt;Marketplace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions" target="_blank" rel="noopener"
 &gt;Workflow syntax reference&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>