<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Darius Hermes</title>
    <description>The latest articles on Forem by Darius Hermes (@dardar_hermes).</description>
    <link>https://forem.com/dardar_hermes</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%2F3946676%2F775dd298-3784-4ed1-93fe-e5884367221f.png</url>
      <title>Forem: Darius Hermes</title>
      <link>https://forem.com/dardar_hermes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dardar_hermes"/>
    <language>en</language>
    <item>
      <title>GitLab CI deploy failed because one token was never documented</title>
      <dc:creator>Darius Hermes</dc:creator>
      <pubDate>Sun, 24 May 2026 16:32:43 +0000</pubDate>
      <link>https://forem.com/dardar_hermes/gitlab-ci-deploy-failed-because-one-token-was-never-documented-4fj0</link>
      <guid>https://forem.com/dardar_hermes/gitlab-ci-deploy-failed-because-one-token-was-never-documented-4fj0</guid>
      <description>&lt;p&gt;A common GitLab CI failure is not that the deploy script is wrong.&lt;/p&gt;

&lt;p&gt;It is that the pipeline expects a variable nobody documented.&lt;/p&gt;

&lt;p&gt;Example:&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;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;

&lt;span class="na"&gt;deploy_production&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&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;alpine:3.20&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;test -n "$DEPLOY_TOKEN"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;echo "Deploying $NEXT_PUBLIC_APP_URL"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./scripts/deploy.sh --token "$DEPLOY_TOKEN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the repo contract says only this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_APP_URL=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;DEPLOY_TOKEN&lt;/code&gt; exists as an assumption in the pipeline, but not in &lt;code&gt;.env.example&lt;/code&gt; or &lt;code&gt;.env.dist&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That means a reviewer can approve the pipeline change without realizing that someone still needs to configure a GitLab CI/CD Variable before deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is deployment drift
&lt;/h2&gt;

&lt;p&gt;The actual secret value belongs in GitLab CI/CD Variables.&lt;/p&gt;

&lt;p&gt;The variable &lt;strong&gt;name&lt;/strong&gt; belongs in the repo's environment contract.&lt;/p&gt;

&lt;p&gt;When those drift apart, the failure usually appears late:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a merge request updates &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;the deploy job references &lt;code&gt;$DEPLOY_TOKEN&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;the env template does not mention it;&lt;/li&gt;
&lt;li&gt;the pipeline reaches deploy time and fails with missing configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is especially easy to miss when an AI coding agent or quick cleanup PR changes CI YAML and the review focuses on application code.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small review habit that catches it
&lt;/h2&gt;

&lt;p&gt;When a merge request changes &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;, deploy scripts, or Docker commands, scan for new env references:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$VARIABLE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;${VARIABLE}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;process.env.VARIABLE&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;deploy CLI flags such as &lt;code&gt;--token "$DEPLOY_TOKEN"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then check whether the variable name is present in the env contract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_APP_URL=
DEPLOY_TOKEN=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The template should not contain the real value. It only documents that the variable is required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automated check
&lt;/h2&gt;

&lt;p&gt;With Secret Coverage, the local check looks like this:&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 @leviro-ai/secret-coverage scan &lt;span class="nt"&gt;--path&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--ci&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a pnpm script wrapper inside the repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm scan &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--path&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--ci&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The useful finding is the variable name and location, not the secret value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DEPLOY_TOKEN is referenced in .gitlab-ci.yml but missing from the env template.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the point: metadata-only deployment readiness, before CI or production discovers the missing contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safe fix
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Add &lt;code&gt;DEPLOY_TOKEN=&lt;/code&gt; to &lt;code&gt;.env.example&lt;/code&gt; or &lt;code&gt;.env.dist&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Configure the real value in GitLab CI/CD Variables.&lt;/li&gt;
&lt;li&gt;Re-run the scan.&lt;/li&gt;
&lt;li&gt;Keep the check in the merge request or CI path so future pipeline edits do not silently add new assumptions.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  PR checklist
&lt;/h2&gt;

&lt;p&gt;For any GitLab pipeline change, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did this MR add a new &lt;code&gt;$VARIABLE&lt;/code&gt; reference?&lt;/li&gt;
&lt;li&gt;Is the variable name documented in &lt;code&gt;.env.example&lt;/code&gt; or &lt;code&gt;.env.dist&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Is the real value configured in GitLab CI/CD Variables before merge?&lt;/li&gt;
&lt;li&gt;Should the check fail the MR with &lt;code&gt;--ci&lt;/code&gt; if a required deployment variable is missing?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is a small habit, but it prevents the frustrating class of deploys that fail because the repo and CI environment stopped agreeing.&lt;/p&gt;




&lt;p&gt;Secret Coverage is a local-first, metadata-only environment/deployment readiness scanner: it checks variable names and config references, not raw secret values.&lt;/p&gt;

&lt;p&gt;Repo/package: &lt;code&gt;@leviro-ai/secret-coverage&lt;/code&gt;&lt;/p&gt;

</description>
      <category>gitlab</category>
    </item>
    <item>
      <title>How I review AI-generated PRs for missing environment variables</title>
      <dc:creator>Darius Hermes</dc:creator>
      <pubDate>Sat, 23 May 2026 10:32:00 +0000</pubDate>
      <link>https://forem.com/dardar_hermes/how-i-review-ai-generated-prs-for-missing-environment-variables-1j9g</link>
      <guid>https://forem.com/dardar_hermes/how-i-review-ai-generated-prs-for-missing-environment-variables-1j9g</guid>
      <description>&lt;p&gt;AI-assisted PRs often change application code, deployment config, and CI at the same time.&lt;/p&gt;

&lt;p&gt;That is useful, but it creates a boring failure mode that is easy to miss in review:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the PR starts using a new environment variable, but &lt;code&gt;.env.example&lt;/code&gt; or &lt;code&gt;.env.dist&lt;/code&gt; is not updated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No raw secret values need to be leaked for this to break a deploy. The problem is metadata drift: the repo now depends on a variable that the declared environment contract does not mention.&lt;/p&gt;

&lt;p&gt;Here is the review flow I use for that kind of PR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario
&lt;/h2&gt;

&lt;p&gt;An AI agent opens a PR that looks reasonable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adds a Stripe-backed checkout deploy step to GitHub Actions;&lt;/li&gt;
&lt;li&gt;adds a Redis-backed worker in Docker Compose;&lt;/li&gt;
&lt;li&gt;updates runtime code paths;&lt;/li&gt;
&lt;li&gt;forgets to update &lt;code&gt;.env.example&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The diff might pass normal code review because each individual change looks small.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start with the env contract
&lt;/h2&gt;

&lt;p&gt;The template file is the contract future contributors and reviewers see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_APP_URL=https://example.com
DATABASE_URL=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This says the project expects &lt;code&gt;NEXT_PUBLIC_APP_URL&lt;/code&gt; and &lt;code&gt;DATABASE_URL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It does not say anything about Stripe or Redis.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Check CI and deployment config, not only source code
&lt;/h2&gt;

&lt;p&gt;The workflow now references a new secret:&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;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DATABASE_URL }}&lt;/span&gt;
  &lt;span class="na"&gt;STRIPE_SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.STRIPE_SECRET_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker Compose also expects Redis:&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DATABASE_URL}&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${REDIS_URL}&lt;/span&gt;

  &lt;span class="na"&gt;worker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${REDIS_URL}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both changes are normal. The bug is that &lt;code&gt;STRIPE_SECRET_KEY&lt;/code&gt; and &lt;code&gt;REDIS_URL&lt;/code&gt; became required deployment inputs without being added to the env template.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Run a metadata-only drift check
&lt;/h2&gt;

&lt;p&gt;In the Secret Coverage repo, these fixtures model the two review findings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm scan &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--path&lt;/span&gt; examples/demos/github-actions-missing-secret &lt;span class="nt"&gt;--ci&lt;/span&gt;
pnpm scan &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--path&lt;/span&gt; examples/demos/docker-compose-missing-redis-url &lt;span class="nt"&gt;--ci&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a consumer repo using the published package:&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 @leviro-ai/secret-coverage scan &lt;span class="nt"&gt;--path&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--ci&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The check compares variable names referenced by CI/CD, Docker, and config files with variable names declared in env templates. It does not need production secret values.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Read the findings like review comments
&lt;/h2&gt;

&lt;p&gt;GitHub Actions drift:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Critical&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="gs"&gt;**STRIPE_SECRET_KEY**&lt;/span&gt; — STRIPE_SECRET_KEY is used in .github/workflows/deploy.yml but missing from an env template.
&lt;span class="p"&gt;  -&lt;/span&gt; Context: &lt;span class="sb"&gt;`.github/workflows/deploy.yml`&lt;/span&gt; · &lt;span class="sb"&gt;`missing-from-template`&lt;/span&gt;
&lt;span class="p"&gt;  -&lt;/span&gt; Fix: Add STRIPE_SECRET_KEY= to an env template and configure the value in your deployment environment.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docker Compose drift:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Critical&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="gs"&gt;**REDIS_URL**&lt;/span&gt; — REDIS_URL is used in docker-compose.yml but missing from an env template.
&lt;span class="p"&gt;  -&lt;/span&gt; Context: &lt;span class="sb"&gt;`docker-compose.yml`&lt;/span&gt; · &lt;span class="sb"&gt;`missing-from-template`&lt;/span&gt;
&lt;span class="p"&gt;  -&lt;/span&gt; Fix: Add REDIS_URL= to an env template and configure the value in your deployment environment.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a useful PR-review signal because it points to the missing contract update, not to a vague deployment risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Ask for the smallest safe fix
&lt;/h2&gt;

&lt;p&gt;The fix is usually a tiny template update:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_APP_URL=https://example.com
DATABASE_URL=
STRIPE_SECRET_KEY=
REDIS_URL=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The real values still belong in the deployment platform:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Actions secrets for CI/CD secrets;&lt;/li&gt;
&lt;li&gt;Docker or Compose runtime environment for service variables;&lt;/li&gt;
&lt;li&gt;Vercel, CircleCI, or another platform's environment settings where relevant.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Do not put raw secret values into &lt;code&gt;.env.example&lt;/code&gt;, docs, screenshots, or review comments.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Add the check before merge
&lt;/h2&gt;

&lt;p&gt;A small CI check can fail the PR before the deploy job discovers the mismatch:&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;secret-coverage&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;pull_request&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="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&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;env-contract&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@v6&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@v6&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="m"&gt;20&lt;/span&gt;
      &lt;span class="pi"&gt;-&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;pnpm dlx @leviro-ai/secret-coverage scan --path . --ci&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  PR review checklist
&lt;/h2&gt;

&lt;p&gt;Use this when an AI-assisted PR touches deployment config, CI, Docker, workers, or framework runtime config:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did the PR add a new &lt;code&gt;process.env.NAME&lt;/code&gt;, &lt;code&gt;${NAME}&lt;/code&gt;, &lt;code&gt;${{ secrets.NAME }}&lt;/code&gt;, or CI &lt;code&gt;env&lt;/code&gt; reference?&lt;/li&gt;
&lt;li&gt;Is every new required variable declared in &lt;code&gt;.env.example&lt;/code&gt;, &lt;code&gt;.env.dist&lt;/code&gt;, or the chosen template file?&lt;/li&gt;
&lt;li&gt;Are demo/docs outputs metadata-only, without raw secret values?&lt;/li&gt;
&lt;li&gt;Does the CI check run before deploy?&lt;/li&gt;
&lt;li&gt;Does the finding tell the contributor which template entry to add?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Secret Coverage is the local-first tool I am using for this check. It is deterministic, metadata-only, and does not need a cloud account.&lt;/p&gt;

&lt;p&gt;Links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/leviro-ai/secret-coverage" rel="noopener noreferrer"&gt;https://github.com/leviro-ai/secret-coverage&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/@leviro-ai/secret-coverage" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@leviro-ai/secret-coverage&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AI-agent PR walkthrough: &lt;a href="https://github.com/leviro-ai/secret-coverage/blob/main/docs/articles/ai-agent-pr-env-review-walkthrough.md" rel="noopener noreferrer"&gt;https://github.com/leviro-ai/secret-coverage/blob/main/docs/articles/ai-agent-pr-env-review-walkthrough.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub Actions demo: &lt;a href="https://github.com/leviro-ai/secret-coverage/tree/main/examples/demos/github-actions-missing-secret" rel="noopener noreferrer"&gt;https://github.com/leviro-ai/secret-coverage/tree/main/examples/demos/github-actions-missing-secret&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docker Compose demo: &lt;a href="https://github.com/leviro-ai/secret-coverage/tree/main/examples/demos/docker-compose-missing-redis-url" rel="noopener noreferrer"&gt;https://github.com/leviro-ai/secret-coverage/tree/main/examples/demos/docker-compose-missing-redis-url&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>ai</category>
      <category>githubactions</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Two tiny deployment drift bugs: env vars added, templates forgotten</title>
      <dc:creator>Darius Hermes</dc:creator>
      <pubDate>Sat, 23 May 2026 02:49:42 +0000</pubDate>
      <link>https://forem.com/dardar_hermes/two-tiny-deployment-drift-bugs-env-vars-added-templates-forgotten-jam</link>
      <guid>https://forem.com/dardar_hermes/two-tiny-deployment-drift-bugs-env-vars-added-templates-forgotten-jam</guid>
      <description>&lt;p&gt;A small deployment failure pattern I keep seeing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A config file starts using a new environment variable or secret.&lt;/li&gt;
&lt;li&gt;The repo's &lt;code&gt;.env.example&lt;/code&gt; or &lt;code&gt;.env.dist&lt;/code&gt; is not updated.&lt;/li&gt;
&lt;li&gt;The mismatch is discovered later, usually during a deploy job, local preview, worker boot, or production config check.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The bug is rarely dramatic in code review. It can be as small as one extra variable in CI/CD or Docker config.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example 1: GitHub Actions secret drift
&lt;/h3&gt;

&lt;p&gt;A workflow starts using a new secret:&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;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DATABASE_URL }}&lt;/span&gt;
  &lt;span class="na"&gt;STRIPE_SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.STRIPE_SECRET_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the env template only documents this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_APP_URL=https://example.com
DATABASE_URL=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;STRIPE_SECRET_KEY&lt;/code&gt; has become an undocumented deployment requirement.&lt;/p&gt;

&lt;p&gt;Run the demo fixture with Secret Coverage:&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 @leviro-ai/secret-coverage scan &lt;span class="nt"&gt;--path&lt;/span&gt; examples/demos/github-actions-missing-secret &lt;span class="nt"&gt;--ci&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the repo itself, the equivalent dev command is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm scan &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--path&lt;/span&gt; examples/demos/github-actions-missing-secret &lt;span class="nt"&gt;--ci&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Secret Coverage Report&lt;/span&gt;

Readiness score: &lt;span class="gs"&gt;**73/100**&lt;/span&gt;

Critical: 1 · Warning: 0 · Info: 1

&lt;span class="gu"&gt;## Critical&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="gs"&gt;**STRIPE_SECRET_KEY**&lt;/span&gt; — STRIPE_SECRET_KEY is used in .github/workflows/deploy.yml but missing from an env template.
&lt;span class="p"&gt;  -&lt;/span&gt; Context: &lt;span class="sb"&gt;`.github/workflows/deploy.yml`&lt;/span&gt; · &lt;span class="sb"&gt;`missing-from-template`&lt;/span&gt;
&lt;span class="p"&gt;  -&lt;/span&gt; Fix: Add STRIPE_SECRET_KEY= to an env template and configure the value in your deployment environment.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example 2: Docker Compose runtime drift
&lt;/h3&gt;

&lt;p&gt;The same thing can happen outside CI. A Compose file starts expecting Redis:&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;APP_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${APP_ENV}&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${DATABASE_URL}&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${REDIS_URL}&lt;/span&gt;

  &lt;span class="na"&gt;worker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${REDIS_URL}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But &lt;code&gt;.env.example&lt;/code&gt; only documents:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Now both the web service and worker depend on &lt;code&gt;REDIS_URL&lt;/code&gt;, but the repository contract does not say so.&lt;/p&gt;

&lt;p&gt;Run the demo fixture:&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 @leviro-ai/secret-coverage scan &lt;span class="nt"&gt;--path&lt;/span&gt; examples/demos/docker-compose-missing-redis-url &lt;span class="nt"&gt;--ci&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Secret Coverage Report&lt;/span&gt;

Readiness score: &lt;span class="gs"&gt;**71/100**&lt;/span&gt;

Critical: 1 · Warning: 0 · Info: 2

&lt;span class="gu"&gt;## Critical&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="gs"&gt;**REDIS_URL**&lt;/span&gt; — REDIS_URL is used in docker-compose.yml but missing from an env template.
&lt;span class="p"&gt;  -&lt;/span&gt; Context: &lt;span class="sb"&gt;`docker-compose.yml`&lt;/span&gt; · &lt;span class="sb"&gt;`missing-from-template`&lt;/span&gt;
&lt;span class="p"&gt;  -&lt;/span&gt; Fix: Add REDIS_URL= to an env template and configure the value in your deployment environment.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is deployment drift: deployment/runtime config expects something the repository's declared env contract does not describe.&lt;/p&gt;

&lt;p&gt;The point is not to read or expose secret values. The check only compares metadata:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;variables documented by env templates;&lt;/li&gt;
&lt;li&gt;variables referenced by CI/CD, Docker, and config files;&lt;/li&gt;
&lt;li&gt;mismatches that should be fixed before deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A minimal fix is to update the env template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# GitHub Actions example
NEXT_PUBLIC_APP_URL=https://example.com
DATABASE_URL=
STRIPE_SECRET_KEY=

# Docker Compose example
APP_ENV=production
DATABASE_URL=
REDIS_URL=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then configure the real values in GitHub Actions secrets, Docker/Compose runtime environment, or the deployment platform.&lt;/p&gt;

&lt;p&gt;This is especially useful when AI-assisted PRs update application code and config quickly, because env contracts are easy to forget during review.&lt;/p&gt;

&lt;p&gt;Secret Coverage is local-first and deterministic. It is not a vault and it does not need a cloud account for this check.&lt;/p&gt;

&lt;p&gt;Links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/@leviro-ai/secret-coverage" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@leviro-ai/secret-coverage&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/leviro-ai/secret-coverage" rel="noopener noreferrer"&gt;https://github.com/leviro-ai/secret-coverage&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub Actions demo: &lt;a href="https://github.com/leviro-ai/secret-coverage/tree/main/examples/demos/github-actions-missing-secret" rel="noopener noreferrer"&gt;https://github.com/leviro-ai/secret-coverage/tree/main/examples/demos/github-actions-missing-secret&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docker Compose demo: &lt;a href="https://github.com/leviro-ai/secret-coverage/tree/main/examples/demos/docker-compose-missing-redis-url" rel="noopener noreferrer"&gt;https://github.com/leviro-ai/secret-coverage/tree/main/examples/demos/docker-compose-missing-redis-url&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>githubactions</category>
      <category>docker</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
