<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Matej Lednický</title>
    <description>The latest articles on DEV Community by Matej Lednický (@mathio).</description>
    <link>https://dev.to/mathio</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2728394%2F8abd3e28-cdb9-4db9-a734-fe6cd6be5461.png</url>
      <title>DEV Community: Matej Lednický</title>
      <link>https://dev.to/mathio</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mathio"/>
    <language>en</language>
    <item>
      <title>GitHub Runner Cleanup: A Reusable Action 🚀🧹💾</title>
      <dc:creator>Matej Lednický</dc:creator>
      <pubDate>Fri, 11 Jul 2025 16:06:39 +0000</pubDate>
      <link>https://dev.to/mathio/github-runner-cleanup-a-reusable-action-5afh</link>
      <guid>https://dev.to/mathio/github-runner-cleanup-a-reusable-action-5afh</guid>
      <description>&lt;p&gt;Some time ago, I wrote a DEV.to article about reclaiming disk space in GitHub Actions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/mathio/squeezing-disk-space-from-github-actions-runners-an-engineers-guide-3pjg"&gt;Squeezing Disk Space from GitHub Actions Runners: An Engineer's Guide 🚀🧹💾&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt; GitHub Action runners come preloaded with a lot of useful software that—well—might not be useful for your use case at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reusable GitHub Action ⚙️
&lt;/h2&gt;

&lt;p&gt;I’ve published a reusable GitHub Action to help you clean up unnecessary tools, packages, and caches—and free up valuable disk space during your CI runs.&lt;/p&gt;

&lt;p&gt;➡️ &lt;strong&gt;Check it out on GitHub Marketplace&lt;/strong&gt;:  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/marketplace/actions/runner-cleanup-action" rel="noopener noreferrer"&gt;https://github.com/marketplace/actions/runner-cleanup-action&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;➡️ &lt;strong&gt;Use it in your GHA workflow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mathio/gha-cleanup@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;remove-browsers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# or false if you need to run tests or automation in browser&lt;/span&gt;
    &lt;span class="na"&gt;verbose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# or false if you do not care about logs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;For full documentation and source code, visit my GitHub repository:  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/mathio/gha-cleanup" rel="noopener noreferrer"&gt;mathio/gha-cleanup&lt;/a&gt;&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>cicd</category>
      <category>github</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Lingo.dev Compiler is here 💚</title>
      <dc:creator>Matej Lednický</dc:creator>
      <pubDate>Tue, 03 Jun 2025 20:24:29 +0000</pubDate>
      <link>https://dev.to/mathio/lingodev-compiler-is-here-3g1h</link>
      <guid>https://dev.to/mathio/lingodev-compiler-is-here-3g1h</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/maxprilutskiy/introducing-lingodev-compiler-localize-a-react-app-without-rewriting-its-code-5anm" class="crayons-story__hidden-navigation-link"&gt;Introducing Lingo.dev Compiler: Localize a React app without rewriting its code&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/maxprilutskiy" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F689136%2Fbfd83da5-0a32-4f35-aada-6fb2e3f80bed.png" alt="maxprilutskiy profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/maxprilutskiy" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Max Prilutskiy
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Max Prilutskiy
                
              
              &lt;div id="story-author-preview-content-2559674" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/maxprilutskiy" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F689136%2Fbfd83da5-0a32-4f35-aada-6fb2e3f80bed.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Max Prilutskiy&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/maxprilutskiy/introducing-lingodev-compiler-localize-a-react-app-without-rewriting-its-code-5anm" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 3 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/maxprilutskiy/introducing-lingodev-compiler-localize-a-react-app-without-rewriting-its-code-5anm" id="article-link-2559674"&gt;
          Introducing Lingo.dev Compiler: Localize a React app without rewriting its code
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/react"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;react&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/maxprilutskiy/introducing-lingodev-compiler-localize-a-react-app-without-rewriting-its-code-5anm" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;45&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/maxprilutskiy/introducing-lingodev-compiler-localize-a-react-app-without-rewriting-its-code-5anm#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              15&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Squeezing Disk Space from GitHub Actions Runners: An Engineer's Guide 🚀🧹💾</title>
      <dc:creator>Matej Lednický</dc:creator>
      <pubDate>Tue, 27 May 2025 19:05:54 +0000</pubDate>
      <link>https://dev.to/mathio/squeezing-disk-space-from-github-actions-runners-an-engineers-guide-3pjg</link>
      <guid>https://dev.to/mathio/squeezing-disk-space-from-github-actions-runners-an-engineers-guide-3pjg</guid>
      <description>&lt;p&gt;Last week I was deploying our &lt;a href="https://lingo.dev/" rel="noopener noreferrer"&gt;Lingo.dev&lt;/a&gt; app as a Docker image from a GitHub Action. And to deploy it, I had to build it inside on a Github-hosted runner. However I hit the limit: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"You are running out of disk space."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Have you ever hit this sudden and mysterious failure in your GitHub Actions workflow too?&lt;/p&gt;

&lt;p&gt;As our CI pipelines grew in complexity and size, we run out of disk space on GitHub-hosted runners. In this practical guide, I’ll show you what fills up the disk, how I analyzed it, and what I did to reclaim enough space to make my builds reliable again.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;💡 tl;dr&lt;/strong&gt; go to chapter 6 and copy the Github Action step to clean up your Github-hosted runner.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. The Limits of GitHub-hosted Runners
&lt;/h2&gt;

&lt;p&gt;GitHub-hosted runners come preloaded with a wide variety of programming languages, SDKs, and tools. This is great for convenience, but the trade-off is bloat. Here's what a typical &lt;code&gt;ubuntu-latest&lt;/code&gt; runner includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Preinstalled languages: Java (multiple JDKs), .NET, Python, Ruby, Go, Swift, Haskell (GHC), Julia, Node.js&lt;/li&gt;
&lt;li&gt;Developer tools: Android SDK, Docker, Azure CLI, Google Cloud SDK, CodeQL, browsers (Chrome, Edge, Firefox), Chromium&lt;/li&gt;
&lt;li&gt;Miscellaneous: PowerShell, Rust toolchains, LLVM, debugging tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Out of ~72 GB total disk, over 50 GB is used before any workflow step runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Hitting the Wall: My Symptoms
&lt;/h2&gt;

&lt;p&gt;I was building and deploying Docker images using a GitHub Actions workflow. Everything worked fine until the job suddenly failed with a disk space error during the Docker build phase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;You are running out of disk space. The runner will stop working when the machine runs out of disk space. Free space left: 54 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prompted me to figure out what was actually eating up all the space.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Measuring the Beast: How Much is Used and Where 🤔
&lt;/h2&gt;

&lt;p&gt;To understand the problem, I added the following steps to the GitHub Actions workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Show disk usage&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;df -h&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Top-level directories&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;du -h -d1 / | sort -hr | head -n &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Detailed file sizes&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;find / -type f -exec du -h {} + 2&amp;gt;/dev/null | sort -hr | head -n &lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what I found:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/usr&lt;/code&gt;: 34 GB — this directory holds most system software and preinstalled runtimes like Java, Swift, and .NET. It's often the largest.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/opt&lt;/code&gt;: 11 GB — contains hosted toolcaches for things like CodeQL, Go, and language-specific tools that GitHub Actions preinstalls.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/mnt&lt;/code&gt;: 4.1 GB — usually used for temporary mounts or ephemeral files. Sometimes includes scratch space used during builds.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/home&lt;/code&gt;: 1.2 GB — stores user-level data, including runner-specific config, caches, and things like rustup toolchains.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Looking closer, it was clear that &lt;code&gt;/usr/share/swift&lt;/code&gt;, &lt;code&gt;/usr/local/.ghcup&lt;/code&gt;, and &lt;code&gt;/usr/local/lib/android&lt;/code&gt; were some of the biggest space hogs.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. What's Hiding in Your Runners?
&lt;/h2&gt;

&lt;p&gt;Most of the disk was taken up by things I didn’t need. Here's a breakdown:&lt;/p&gt;

&lt;h3&gt;
  
  
  Languages and Runtimes
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Approx Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/usr/lib/jvm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~1.5 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.NET&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/usr/share/dotnet&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~1.5 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Swift&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/usr/share/swift&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~2.5 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Haskell&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/usr/local/.ghcup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~3 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/home/runner/.rustup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~1.5 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Julia&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/usr/local/julia*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~500 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android SDK&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/usr/local/lib/android/sdk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~6 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browsers&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/usr/local/share/chromium&lt;/code&gt;, etc.&lt;/td&gt;
&lt;td&gt;~1.5 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Developer Tools
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Approx Size&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CodeQL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/opt/hostedtoolcache/CodeQL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~2 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PowerShell&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/usr/local/share/powershell&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~800 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure CLI&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/opt/az&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~200 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google SDK&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/usr/lib/google-cloud-sdk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~200 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  5. What Can You Safely Remove? 🗑️
&lt;/h2&gt;

&lt;p&gt;Depending on your use case, you can remove different tools. Since I was building and deploying Docker images for a Node.js application, I didn’t need most preinstalled SDKs and languages.&lt;/p&gt;

&lt;p&gt;Here are a few examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js project&lt;/strong&gt;: remove Java, .NET, Swift, Haskell, Android SDK, CodeQL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python scripts&lt;/strong&gt;: remove Java, .NET, Android, Browsers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Java application&lt;/strong&gt;: remove Swift, Haskell, CodeQL, Node.js, Android SDK&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In general, you can also remove browsers like Chromium if you’re not using them for end-to-end testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. How to Automate Cleanup in Your CI 🚮
&lt;/h2&gt;

&lt;p&gt;I added this step to the top of my GitHub Actions workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Aggressive cleanup&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;# Remove Java (JDKs)&lt;/span&gt;
    &lt;span class="s"&gt;sudo rm -rf /usr/lib/jvm&lt;/span&gt;

    &lt;span class="s"&gt;# Remove .NET SDKs&lt;/span&gt;
    &lt;span class="s"&gt;sudo rm -rf /usr/share/dotnet&lt;/span&gt;

    &lt;span class="s"&gt;# Remove Swift toolchain&lt;/span&gt;
    &lt;span class="s"&gt;sudo rm -rf /usr/share/swift&lt;/span&gt;

    &lt;span class="s"&gt;# Remove Haskell (GHC)&lt;/span&gt;
    &lt;span class="s"&gt;sudo rm -rf /usr/local/.ghcup&lt;/span&gt;

    &lt;span class="s"&gt;# Remove Julia&lt;/span&gt;
    &lt;span class="s"&gt;sudo rm -rf /usr/local/julia*&lt;/span&gt;

    &lt;span class="s"&gt;# Remove Android SDKs&lt;/span&gt;
    &lt;span class="s"&gt;sudo rm -rf /usr/local/lib/android&lt;/span&gt;

    &lt;span class="s"&gt;# Remove Chromium (optional if not using for browser tests)&lt;/span&gt;
    &lt;span class="s"&gt;sudo rm -rf /usr/local/share/chromium&lt;/span&gt;

    &lt;span class="s"&gt;# Remove Microsoft/Edge and Google Chrome builds&lt;/span&gt;
    &lt;span class="s"&gt;sudo rm -rf /opt/microsoft /opt/google&lt;/span&gt;

    &lt;span class="s"&gt;# Remove Azure CLI&lt;/span&gt;
    &lt;span class="s"&gt;sudo rm -rf /opt/az&lt;/span&gt;

    &lt;span class="s"&gt;# Remove PowerShell&lt;/span&gt;
    &lt;span class="s"&gt;sudo rm -rf /usr/local/share/powershell&lt;/span&gt;

    &lt;span class="s"&gt;# Remove CodeQL and other toolcaches&lt;/span&gt;
    &lt;span class="s"&gt;sudo rm -rf /opt/hostedtoolcache&lt;/span&gt;

    &lt;span class="s"&gt;docker system prune -af || true&lt;/span&gt;
    &lt;span class="s"&gt;docker builder prune -af || true&lt;/span&gt;
    &lt;span class="s"&gt;df -h&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This alone saved more than 20 GB of space.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Bonus: Logging &amp;amp; Debugging Disk Usage Over Time 📊
&lt;/h2&gt;

&lt;p&gt;To keep an eye on disk usage trends or debug failures, I uploaded disk logs as artifacts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload disk logs&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v3&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;disk-logs&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;logs/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I generate the logs using tools like &lt;code&gt;df&lt;/code&gt;, &lt;code&gt;du&lt;/code&gt;, and &lt;code&gt;find&lt;/code&gt;, and use them for future audits.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Final thoughts
&lt;/h2&gt;

&lt;p&gt;Disk space can quietly break your CI/CD pipelines. GitHub-hosted runners are filled with helpful tools, but many are unnecessary for your project. By auditing and removing what's not needed, I was able to consistently reclaim 15–25 GB of space and run Docker builds reliably.&lt;/p&gt;

&lt;p&gt;Feel free to use the &lt;strong&gt;Aggressive cleanup&lt;/strong&gt; step (see above) in your own Github Action.&lt;/p&gt;

&lt;p&gt;If you're running into disk issues, don’t jump to larger runners. Start by cleaning up what you don’t use.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://x.com/mathio28" rel="noopener noreferrer"&gt;Let me know on X @mathio28&lt;/a&gt; if you had a similar issue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F48nmd4epzk7qlsb12uj5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F48nmd4epzk7qlsb12uj5.jpg" alt="Github Action" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Images:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Photo by &lt;a href="https://unsplash.com/@hongqi?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Bruce  Hong&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/black-and-green-computer-hard-disk-drive-0RyLIf84mXU?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Photo by &lt;a href="https://unsplash.com/@synkevych?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Roman Synkevych&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/black-and-white-penguin-toy-wX2L8L-fGeA?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>github</category>
      <category>githubactions</category>
      <category>cicd</category>
      <category>webdev</category>
    </item>
    <item>
      <title>To infinity and beyond 🚀</title>
      <dc:creator>Matej Lednický</dc:creator>
      <pubDate>Wed, 19 Feb 2025 16:06:17 +0000</pubDate>
      <link>https://dev.to/mathio/to-infinity-and-beyond-4n1n</link>
      <guid>https://dev.to/mathio/to-infinity-and-beyond-4n1n</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/maxprilutskiy" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F689136%2F221f3d89-80d4-4db5-91ee-2d69eb178bcd.png" alt="maxprilutskiy"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/maxprilutskiy/we-just-raised-42-to-keep-building-our-hackathon-project-19e1" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;We just raised $4.2 to keep building our hackathon project!&lt;/h2&gt;
      &lt;h3&gt;Max Prilutskiy ・ Feb 19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#webdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#hackathon&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>hackathon</category>
    </item>
    <item>
      <title>Building Cross-Platform CI/CD Actions with Docker</title>
      <dc:creator>Matej Lednický</dc:creator>
      <pubDate>Wed, 29 Jan 2025 15:51:09 +0000</pubDate>
      <link>https://dev.to/mathio/building-cross-platform-cicd-actions-with-docker-3c9o</link>
      <guid>https://dev.to/mathio/building-cross-platform-cicd-actions-with-docker-3c9o</guid>
      <description>&lt;p&gt;Last week I got hit by a headache - our perfectly tuned &lt;a href="http://Lingo.dev" rel="noopener noreferrer"&gt;Lingo.dev&lt;/a&gt; GitHub Actions workflow couldn't run on a contributor's GitLab instance. Then I realized I’d actually like to run the same automation everywhere, regardless of the platform.&lt;/p&gt;

&lt;p&gt;So I went on a quest to build a cross-platform CI automation that runs on GitHub, GitLab, and Bitbucket (and possibly others too!). The solution started as a simple GitHub Action but evolved into something more powerful when we needed to support multiple code hosting platforms. &lt;/p&gt;

&lt;p&gt;I will walk you through the exact process:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;starting simple, I will showcase how Github Actions work&lt;/li&gt;
&lt;li&gt;leveling up to build reusable Docker image&lt;/li&gt;
&lt;li&gt;finally I will show you how to run this on each platform&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Follow the steps to build and ship your first cross-platform action. Or bookmark the article for later.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;tl;dr see the template repository 👇👇👇&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  1. Starting Simple: Run JavaScript in GitHub Actions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Running a GitHub Action
&lt;/h3&gt;

&lt;p&gt;Let's start with the simplest possible GitHub Action - one that runs a JavaScript file. First, create &lt;code&gt;index.js&lt;/code&gt; in the root of your repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello World&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now create a workflow file &lt;code&gt;.github/workflows/hello.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Hello World&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;20"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Say Hello&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node index.js&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This action will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Trigger on push to main branch&lt;/li&gt;
&lt;li&gt;Check out your repository&lt;/li&gt;
&lt;li&gt;Set up Node.js environment&lt;/li&gt;
&lt;li&gt;Run your script&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Making it Reusable
&lt;/h3&gt;

&lt;p&gt;Now, let's make this action reusable by moving it to a separate repository. Create a new GitHub repository (e.g., &lt;code&gt;hello-world-action&lt;/code&gt; like &lt;a href="https://github.com/mathio/hello-world-action" rel="noopener noreferrer"&gt;my example here&lt;/a&gt;) with these files:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;index.js&lt;/code&gt; (same as before):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello World&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;action.yml&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;World&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Action"&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;that&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;says&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;hello"&lt;/span&gt;
&lt;span class="na"&gt;runs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;using&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node20"&lt;/span&gt;
  &lt;span class="na"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;index.js"&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Now you can use this action in any repository by referencing it in your workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Use Hello Action&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Say Hello&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-username/hello-world-action@main&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The key differences are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The action lives in its own repository&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;action.yml&lt;/code&gt; defines how to run the action&lt;/li&gt;
&lt;li&gt;Other repositories can reference it using &lt;code&gt;uses: your-username/hello-world-action@ref&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  2. Leveling Up: Dockerized TypeScript Action
&lt;/h2&gt;

&lt;p&gt;Now, let's create a more sophisticated action that runs TypeScript code. We'll need several files:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Initialize the project and set up TypeScript:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm init                &lt;span class="c"&gt;# Creates package.json&lt;/span&gt;
pnpm add &lt;span class="nt"&gt;-D&lt;/span&gt; typescript   &lt;span class="c"&gt;# Install TypeScript as dev dependency&lt;/span&gt;

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

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update your &lt;code&gt;package.json&lt;/code&gt; to add the build script:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then rename our &lt;code&gt;index.js&lt;/code&gt; to &lt;code&gt;index.ts&lt;/code&gt; to use TypeScript instead of JavaScript and move it to the &lt;code&gt;src&lt;/code&gt; directory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create &lt;code&gt;tsconfig.json&lt;/code&gt; to configure TypeScript compilation:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ES2022"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"commonjs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rootDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"esModuleInterop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"skipLibCheck"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"forceConsistentCasingInFileNames"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exclude"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"node_modules"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"**/*.test.ts"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

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

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:20-slim

WORKDIR /app

# Install pnpm
RUN npm install -g pnpm

# Copy package files
COPY package.json pnpm-lock.yaml tsconfig.json /app

# Copy source code
COPY src /app/src

# Install dependencies
RUN pnpm install

# Build TypeScript code
RUN pnpm build

# Run the action
ENTRYPOINT ["node", "./build/index.js"]

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Redefine the action in &lt;code&gt;action.yml&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;World&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;TypeScript&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Action"&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;cross-platform&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;TypeScript&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;example"&lt;/span&gt;
&lt;span class="na"&gt;runs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;using&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docker"&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Dockerfile"&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running Docker Image Locally
&lt;/h3&gt;

&lt;p&gt;To build and run the image defined in Dockerfile locally, you need &lt;a href="https://www.docker.com/get-started/" rel="noopener noreferrer"&gt;Docker Desktop app&lt;/a&gt;. Then, assuming Docker is running locally, you can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build the image:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; hello-world-action &lt;span class="nb"&gt;.&lt;/span&gt;

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

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Run it:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run hello-world-action

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Cross-Platform Support
&lt;/h2&gt;

&lt;p&gt;Now comes the most interesting part - running your action on other platforms. We'll need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build and push our Docker image to a registry&lt;/li&gt;
&lt;li&gt;Reference it in platform-specific configurations&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Building and Publishing Docker Image
&lt;/h3&gt;

&lt;p&gt;First, create a workflow to build and push the Docker image on every release. We will be using GitHub Container Registry (GHCR) that comes with your GitHub (free for public repositories, 500MB for private repositories on free plan).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish Docker image&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push_to_registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login to GitHub Container Registry&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.actor }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and push Docker image&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/${{ github.repository }}:latest&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Github Actions
&lt;/h3&gt;

&lt;p&gt;To use this action in GitHub Actions of another repository, create a workflow file in &lt;code&gt;.github/workflows/hello.yml&lt;/code&gt; (see &lt;a href="https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions" rel="noopener noreferrer"&gt;GitHub Actions workflow syntax documentation&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Hello World&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;master&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-username/hello-world-action@main&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;If you need to run this action on GitHub Actions only, there is no need to build and push the Docker image. GitHub Actions will build the Docker container directly from the Dockerfile specified in your &lt;code&gt;action.yml&lt;/code&gt; file on each workflow run. This is more efficient since it avoids the extra steps of pushing to and pulling from a container registry. However, if you plan to use this action on other CI platforms like GitLab or Bitbucket, you'll need to publish the Docker image as shown above.&lt;/p&gt;

&lt;p&gt;GitHub's free plan offers the most generous &lt;a href="https://docs.github.com/en/billing/managing-billing-for-your-products/managing-billing-for-github-actions/about-billing-for-github-actions" rel="noopener noreferrer"&gt;CI/CD minutes&lt;/a&gt; allocation among the three platforms. It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;unlimited minutes for public repositories&lt;/li&gt;
&lt;li&gt;2,000 minutes/month for private repositories&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  GitLab CI Configuration
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; (see &lt;a href="https://docs.gitlab.com/ee/ci/" rel="noopener noreferrer"&gt;GitLab CI/CD documentation&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/your-username/hello-world-action:latest&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Done"&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_PIPELINE_SOURCE == "push" &amp;amp;&amp;amp; $CI_COMMIT_BRANCH == "main"&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Your Gitlab free plan includes 400 CI/CD &lt;a href="https://docs.gitlab.com/ee/ci/pipelines/compute_minutes.html" rel="noopener noreferrer"&gt;compute minutes&lt;/a&gt; per month regardless if the repository is public or private.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bitbucket Pipelines
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;bitbucket-pipelines.yml&lt;/code&gt; (see &lt;a href="https://support.atlassian.com/bitbucket-cloud/docs/bitbucket-pipelines-configuration-reference/" rel="noopener noreferrer"&gt;Bitbucket Pipelines documentation&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker:latest&lt;/span&gt;

&lt;span class="na"&gt;pipelines&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/your-username/hello-world-action:latest&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Done"&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Bitbucket's free plan includes only 50 &lt;a href="https://www.atlassian.com/blog/bitbucket/everything-you-need-to-know-about-build-minutes-in-bitbucket-pipelines" rel="noopener noreferrer"&gt;build minutes&lt;/a&gt; per month, regardless if the repository is public or private, making it the lowest free tier of all three platforms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other CI/CD Platforms
&lt;/h3&gt;

&lt;p&gt;Since we published our Docker image in a public Github Container Registry, we can run this on any platform that supports Docker images. This is supported by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Circle CI (&lt;a href="https://circleci.com/docs/using-docker/" rel="noopener noreferrer"&gt;docs&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Travis CI (&lt;a href="https://docs.travis-ci.com/user/docker/" rel="noopener noreferrer"&gt;docs&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://twitter.com/mathio28" rel="noopener noreferrer"&gt;Let me know&lt;/a&gt; if you run this somewhere else, I am curious about your use case!&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 What can you do with this?
&lt;/h2&gt;

&lt;p&gt;We built &lt;a href="https://github.com/lingodotdev/lingo.dev" rel="noopener noreferrer"&gt;an action to run automated Lingo.dev localization for your web and mobile apps&lt;/a&gt; on any CI platform. On every commit it updates translations in your entire repository using Lingo.dev localization engine - either as a new commit or by opening a pull request (&lt;a href="https://docs.lingo.dev/ci-action/overview" rel="noopener noreferrer"&gt;docs&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Example for GitHub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lingodotdev/lingo.dev@main&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;api-key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.LINGODOTDEV_API_KEY }}&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;pull-request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;true'&lt;/span&gt; &lt;span class="c1"&gt;# or false, we don't judge&lt;/span&gt;

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

&lt;/div&gt;






&lt;p&gt;You can build reusable actions and easily integrate them into your workflows regardless of the code hosting platform you are using. Here are some ideas for you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cross-platform test reporter (orchestrating end-to-end testing session)&lt;/li&gt;
&lt;li&gt;custom code quality checker (think formatting, linting, testing)&lt;/li&gt;
&lt;li&gt;release notes generator (how about publishing a changelog on your website?)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What would you use it for?&lt;/p&gt;

&lt;h2&gt;
  
  
  💥 Bonus: Template repository
&lt;/h2&gt;

&lt;p&gt;If you don't feel like reading the rest of the article, you can find a template starter repository with all the code here. It contains the action - you can run it on GitHub, GitLab and Bitbucket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mathio/awesome-action-starter" rel="noopener noreferrer"&gt;https://github.com/mathio/awesome-action-starter&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Tip: Clone the repo and make it a starting point for your own action:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/mathio/awesome-action-starter.git my-action
&lt;span class="nb"&gt;cd &lt;/span&gt;my-action
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;…or just &lt;a href="https://github.com/new?template_name=awesome-action-starter&amp;amp;template_owner=mathio" rel="noopener noreferrer"&gt;create a new repository from my template&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then just implement your own action logic in &lt;code&gt;src/index.ts&lt;/code&gt; file.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;We've seen how to evolve from a simple shell-based GitHub Action to a sophisticated TypeScript action that can run anywhere. The key takeaways are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start simple with shell commands to validate your automation logic&lt;/li&gt;
&lt;li&gt;Dockerize your action to make it portable&lt;/li&gt;
&lt;li&gt;Use container registries to distribute your action across platforms&lt;/li&gt;
&lt;li&gt;Adapt the configuration for each platform while keeping the core logic in Docker&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach gives you the flexibility to run your automation anywhere while maintaining a single codebase. Happy automating!&lt;/p&gt;




&lt;p&gt;I occasionally post about tech stuff and new &lt;a href="https://twitter.com/lingodotdev" rel="noopener noreferrer"&gt;Lingo.dev&lt;/a&gt; features on Twitter as &lt;a href="https://twitter.com/mathio28" rel="noopener noreferrer"&gt;@mathio28&lt;/a&gt;. Let's keep in touch.&lt;/p&gt;

</description>
      <category>github</category>
      <category>javascript</category>
      <category>docker</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
