<?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: Jordach M. Makaya</title>
    <description>The latest articles on DEV Community by Jordach M. Makaya (@jordachmakaya).</description>
    <link>https://dev.to/jordachmakaya</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3994592%2Fd668cf9f-b247-43a9-93cc-fb0785ca30af.png</url>
      <title>DEV Community: Jordach M. Makaya</title>
      <link>https://dev.to/jordachmakaya</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jordachmakaya"/>
    <language>en</language>
    <item>
      <title>I Built a CLI to Stop Missing Env Vars from Breaking Deployments</title>
      <dc:creator>Jordach M. Makaya</dc:creator>
      <pubDate>Sat, 20 Jun 2026 20:24:59 +0000</pubDate>
      <link>https://dev.to/jordachmakaya/i-built-a-cli-to-stop-missing-env-vars-from-breaking-deployments-59ke</link>
      <guid>https://dev.to/jordachmakaya/i-built-a-cli-to-stop-missing-env-vars-from-breaking-deployments-59ke</guid>
      <description>&lt;p&gt;Environment variables look boring until they break a deployment.&lt;/p&gt;

&lt;p&gt;I kept running into the same kind of problem while building TypeScript systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a variable exists locally&lt;/li&gt;
&lt;li&gt;the CI pipeline does not have it&lt;/li&gt;
&lt;li&gt;the deployment provider does not have it&lt;/li&gt;
&lt;li&gt;the service starts anyway&lt;/li&gt;
&lt;li&gt;the real failure appears later, far away from the source&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bug is usually not complex.&lt;/p&gt;

&lt;p&gt;The debugging session is.&lt;/p&gt;

&lt;p&gt;You end up checking &lt;code&gt;.env&lt;/code&gt; files, CI variables, GitHub Actions secrets, GitLab variables, deployment dashboards, workflow files, and trying to understand which environment is missing which key.&lt;/p&gt;

&lt;p&gt;At some point I got tired of doing that manually, so I built a small CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;@hardmachinelabs/env-sync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Documentation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://jordachmakaya.github.io/env-sync/" rel="noopener noreferrer"&gt;https://jordachmakaya.github.io/env-sync/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Package:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@hardmachinelabs/env-sync" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@hardmachinelabs/env-sync&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repository:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jordachmakaya/env-sync" rel="noopener noreferrer"&gt;https://github.com/jordachmakaya/env-sync&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Most projects start simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.env
.env.example
README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the project grows.&lt;/p&gt;

&lt;p&gt;You add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;staging&lt;/li&gt;
&lt;li&gt;production&lt;/li&gt;
&lt;li&gt;CI&lt;/li&gt;
&lt;li&gt;preview environments&lt;/li&gt;
&lt;li&gt;multiple services&lt;/li&gt;
&lt;li&gt;multiple packages&lt;/li&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;li&gt;GitLab CI&lt;/li&gt;
&lt;li&gt;deployment providers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And suddenly &lt;code&gt;.env.example&lt;/code&gt; is no longer enough.&lt;/p&gt;

&lt;p&gt;It documents what should exist.&lt;/p&gt;

&lt;p&gt;It does not guarantee that the variables actually exist where the app runs.&lt;/p&gt;

&lt;p&gt;That gap is where a lot of avoidable deployment failures happen.&lt;/p&gt;




&lt;h2&gt;
  
  
  The flow
&lt;/h2&gt;

&lt;p&gt;Here is the basic idea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local project
     |
     v
Discover .env files
     |
     v
Select environment
example: production
     |
     v
Map variable names
     |
     v
Preview changes
--dry-run
     |
     v
Review output
     |
     +--&amp;gt; Looks wrong
     |        |
     |        v
     |   Fix .env files
     |   or naming rules
     |        |
     |        v
     |   Run dry-run again
     |
     +--&amp;gt; Looks correct
              |
              v
        Select provider
              |
      +-------+--------+
      |                |
      v                v
 GitHub Actions    GitLab CI/CD
 secrets           variables
 via gh CLI        via REST API
      |                |
      +-------+--------+
              |
              v
 Secrets created or updated
              |
              v
 CI/CD has the expected variables
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is not the sync itself.&lt;/p&gt;

&lt;p&gt;The important part is the review step before the sync.&lt;/p&gt;

&lt;p&gt;Secrets are not something I want a tool to change silently.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I wanted
&lt;/h2&gt;

&lt;p&gt;I did not want a big secret-management platform.&lt;/p&gt;

&lt;p&gt;I wanted a focused tool that could answer a simple question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Are the environment variables my project needs actually present in the place where my pipeline runs?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And then, when I explicitly ask for it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Can you sync them for me?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I built &lt;code&gt;env-sync&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;At a high level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dlx @hardmachinelabs/env-sync &lt;span class="nt"&gt;--provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;github &lt;span class="nt"&gt;--env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="nt"&gt;--sync-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI discovers &lt;code&gt;.env&lt;/code&gt; files, maps variables, and syncs them to the selected provider.&lt;/p&gt;

&lt;p&gt;Current targets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Actions secrets&lt;/li&gt;
&lt;li&gt;GitLab CI/CD variables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also supports dry-run mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dlx @hardmachinelabs/env-sync &lt;span class="nt"&gt;--provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;github &lt;span class="nt"&gt;--env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="nt"&gt;--sync-only&lt;/span&gt; &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dry-run is the first command I expect people to run.&lt;/p&gt;

&lt;p&gt;Not the real sync.&lt;/p&gt;




&lt;h2&gt;
  
  
  GitHub example
&lt;/h2&gt;

&lt;p&gt;For GitHub, the CLI uses the GitHub CLI.&lt;/p&gt;

&lt;p&gt;That means you should have &lt;code&gt;gh&lt;/code&gt; installed and authenticated before syncing secrets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh auth status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run a dry-run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dlx @hardmachinelabs/env-sync &lt;span class="nt"&gt;--provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;github &lt;span class="nt"&gt;--env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="nt"&gt;--sync-only&lt;/span&gt; &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the output looks correct, remove &lt;code&gt;--dry-run&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dlx @hardmachinelabs/env-sync &lt;span class="nt"&gt;--provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;github &lt;span class="nt"&gt;--env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="nt"&gt;--sync-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--sync-only&lt;/code&gt; flag is useful when your workflows already reference the expected secret names and you only want to sync repository secrets.&lt;/p&gt;

&lt;p&gt;There is also a GitHub-specific &lt;code&gt;--workflows-only&lt;/code&gt; mode if you want to inspect workflow YAML patching separately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dlx @hardmachinelabs/env-sync &lt;span class="nt"&gt;--provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;github &lt;span class="nt"&gt;--env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="nt"&gt;--workflows-only&lt;/span&gt; &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why monorepos matter
&lt;/h2&gt;

&lt;p&gt;This problem gets worse in monorepos.&lt;/p&gt;

&lt;p&gt;You may have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apps/web/.env
apps/api/.env
packages/worker/.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Different services may reuse generic names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL
REDIS_URL
API_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is fine locally.&lt;/p&gt;

&lt;p&gt;It becomes risky when everything is pushed into a shared CI/CD namespace.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;env-sync&lt;/code&gt; is monorepo-aware and can apply namespacing rules to reduce collisions between packages.&lt;/p&gt;

&lt;p&gt;The goal is not to be clever.&lt;/p&gt;

&lt;p&gt;The goal is to make the final secret names predictable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example output
&lt;/h2&gt;

&lt;p&gt;This is the kind of output I want from a tool touching secrets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;────────────────────────────────────────────────────
🔍 env-sync  |  env: production  |  dry-run: false
────────────────────────────────────────────────────
🔍 Found 1 .env file(s)
🔍 Mapped 9 secret(s)
🔍 Provider: github
🔍 Syncing GitHub Actions secrets...
✅ Updated secret: NUXT_PUBLIC_POSTHOG_KEY
✅ Updated secret: NUXT_PUBLIC_POSTHOG_HOST
✅ Updated secret: TWENTY_BASE_URL
✅ Updated secret: TWENTY_API_KEY
✅ Updated secret: TWENTY_PORTFOLIO_REQUEST_OBJECT
✅ Updated secret: DOKPLOY_WEBHOOK_URL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple.&lt;/p&gt;

&lt;p&gt;Visible.&lt;/p&gt;

&lt;p&gt;No guessing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;

&lt;p&gt;You can install it as a dev dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add &lt;span class="nt"&gt;-D&lt;/span&gt; @hardmachinelabs/env-sync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or run it directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dlx @hardmachinelabs/env-sync &lt;span class="nt"&gt;--provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;github &lt;span class="nt"&gt;--env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production &lt;span class="nt"&gt;--sync-only&lt;/span&gt; &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a single file instead of auto-discovery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dlx @hardmachinelabs/env-sync &lt;span class="nt"&gt;--provider&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;github &lt;span class="nt"&gt;--env-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;packages/blog/.env &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Design constraints
&lt;/h2&gt;

&lt;p&gt;I tried to keep the tool boring on purpose.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Dry-run first
&lt;/h3&gt;

&lt;p&gt;I want to inspect changes before anything is written.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Explicit provider support
&lt;/h3&gt;

&lt;p&gt;Right now it supports GitHub and GitLab because those are the providers I actually use.&lt;/p&gt;

&lt;p&gt;I prefer a smaller tool that handles real providers properly over a generic abstraction that hides too much.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. No hidden magic
&lt;/h3&gt;

&lt;p&gt;The CLI should print what it found, what it mapped, and what it synced.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Small dependency surface
&lt;/h3&gt;

&lt;p&gt;Deployment tooling should not bring unnecessary supply-chain risk into a project.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this is not
&lt;/h2&gt;

&lt;p&gt;It is not a replacement for Vault, Doppler, Infisical, AWS Secrets Manager, or any serious enterprise secret-management system.&lt;/p&gt;

&lt;p&gt;It is not trying to solve every secret-management problem.&lt;/p&gt;

&lt;p&gt;It is a focused CLI for a common failure mode:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;local env, CI env, and provider secrets drifting apart.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is the scope.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security notes
&lt;/h2&gt;

&lt;p&gt;A tool that touches secrets needs conservative usage.&lt;/p&gt;

&lt;p&gt;The basic rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;do not commit &lt;code&gt;.env&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;do not paste real secrets into issues, pull requests, screenshots, or docs&lt;/li&gt;
&lt;li&gt;run dry-run first&lt;/li&gt;
&lt;li&gt;review generated secret names&lt;/li&gt;
&lt;li&gt;review workflow diffs before committing them&lt;/li&gt;
&lt;li&gt;verify provider permissions before syncing production secrets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tool can help reduce drift.&lt;/p&gt;

&lt;p&gt;It cannot make unsafe secret practices safe.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I published it
&lt;/h2&gt;

&lt;p&gt;I am preparing to publish more of the small tools that came out of building larger systems.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;env-sync&lt;/code&gt; is one of them.&lt;/p&gt;

&lt;p&gt;It came from a real operational pain point, not from trying to invent a library idea.&lt;/p&gt;

&lt;p&gt;If you manage TypeScript apps, monorepos, GitHub Actions, or GitLab CI/CD, I would appreciate feedback:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the scope clear?&lt;/li&gt;
&lt;li&gt;Would this fit your workflow?&lt;/li&gt;
&lt;li&gt;What would make you trust or not trust a tool that syncs secrets?&lt;/li&gt;
&lt;li&gt;Which provider should be supported next?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am especially interested in feedback from people who have been burned by missing env vars in CI or production.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>devops</category>
      <category>github</category>
      <category>cli</category>
    </item>
  </channel>
</rss>
