<?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: depmedicdev-byte</title>
    <description>The latest articles on DEV Community by depmedicdev-byte (@depmedicdevbyte).</description>
    <link>https://dev.to/depmedicdevbyte</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%2F3901311%2F0eadbbf0-f410-447d-b3da-c1be7981a26d.png</url>
      <title>DEV Community: depmedicdev-byte</title>
      <link>https://dev.to/depmedicdevbyte</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/depmedicdevbyte"/>
    <language>en</language>
    <item>
      <title>I scanned 5 popular OSS repos in 5 minutes. Here's what I found.</title>
      <dc:creator>depmedicdev-byte</dc:creator>
      <pubDate>Tue, 28 Apr 2026 03:06:33 +0000</pubDate>
      <link>https://dev.to/depmedicdevbyte/i-scanned-5-popular-oss-repos-in-5-minutes-heres-what-i-found-3f9o</link>
      <guid>https://dev.to/depmedicdevbyte/i-scanned-5-popular-oss-repos-in-5-minutes-heres-what-i-found-3f9o</guid>
      <description>&lt;p&gt;Earlier today I shipped &lt;a href="https://depmedicdev-byte.github.io/scan.html" rel="noopener noreferrer"&gt;&lt;code&gt;scan.html&lt;/code&gt;&lt;/a&gt;: a one-page in-browser tool that takes any public GitHub repo URL, fetches its &lt;code&gt;.github/workflows/*.yml&lt;/code&gt;, and returns a per-workflow report using &lt;a href="https://www.npmjs.com/package/ci-doctor" rel="noopener noreferrer"&gt;&lt;code&gt;ci-doctor&lt;/code&gt;&lt;/a&gt; (14 rules) and &lt;a href="https://www.npmjs.com/package/gha-budget" rel="noopener noreferrer"&gt;&lt;code&gt;gha-budget&lt;/code&gt;&lt;/a&gt; (per-job pricing). Runs entirely client-side via the GitHub public API. No signup, nothing uploaded.&lt;/p&gt;

&lt;p&gt;To make sure it actually works on real-world repos and not just on the canned examples I built it against, I picked 5 well-known npm-ecosystem repos that I had not specifically optimized for, and ran them through. All 5 are maintained by experienced engineers. None of these are random small repos; they all matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5 repos
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Repo&lt;/th&gt;
&lt;th&gt;Workflows&lt;/th&gt;
&lt;th&gt;Per-run $&lt;/th&gt;
&lt;th&gt;Modeled $/mo*&lt;/th&gt;
&lt;th&gt;Findings (err / warn / info)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/axios/axios" rel="noopener noreferrer"&gt;axios/axios&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;$2.62&lt;/td&gt;
&lt;td&gt;$2,362&lt;/td&gt;
&lt;td&gt;40 (12 / 27 / 1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/eslint/eslint" rel="noopener noreferrer"&gt;eslint/eslint&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;$1.54&lt;/td&gt;
&lt;td&gt;$1,382&lt;/td&gt;
&lt;td&gt;46 (0 / 40 / 6)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/vitejs/vite" rel="noopener noreferrer"&gt;vitejs/vite&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;$1.09&lt;/td&gt;
&lt;td&gt;$979&lt;/td&gt;
&lt;td&gt;26 (0 / 25 / 1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/prettier/prettier" rel="noopener noreferrer"&gt;prettier/prettier&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;$1.02&lt;/td&gt;
&lt;td&gt;$922&lt;/td&gt;
&lt;td&gt;32 (6 / 23 / 3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/sveltejs/svelte" rel="noopener noreferrer"&gt;sveltejs/svelte&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;$0.70&lt;/td&gt;
&lt;td&gt;$634&lt;/td&gt;
&lt;td&gt;14 (0 / 10 / 4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;50&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$6.97&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$6,279&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;158 (18 / 125 / 15)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;*Modeled at 30 runs/day, 8 min/job, on standard &lt;code&gt;ubuntu-latest&lt;/code&gt; GitHub-hosted runner pricing. Real spend depends on actual run frequency, runner choice, and OSS rate-limit credits. The point of the column is comparison, not accusation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same 3 rules show up in all 5 repos
&lt;/h2&gt;

&lt;p&gt;This is the part I find genuinely interesting. These repos have nothing in common architecturally - vite is a bundler, axios is an HTTP client, eslint is a static analyzer, etc. - but the top-3 ci-doctor findings are nearly identical across all of them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;missing-timeout&lt;/code&gt;&lt;/strong&gt; (76 hits across 5 repos). No &lt;code&gt;timeout-minutes:&lt;/code&gt; on jobs, so a hung step bills until GitHub's 6-hour default cap. Every repo has this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;missing-concurrency&lt;/code&gt;&lt;/strong&gt; (20 hits). Push 3 commits to a PR in 30 seconds, you get 3 stacked CI runs and GitHub bills all 3. &lt;code&gt;concurrency:&lt;/code&gt; with &lt;code&gt;cancel-in-progress: true&lt;/code&gt; kills the first 2 in milliseconds. Free 30-50% CI savings on PR-heavy repos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;missing-cache&lt;/code&gt;&lt;/strong&gt; (16 hits, mostly in eslint). &lt;code&gt;actions/setup-node&lt;/code&gt; without &lt;code&gt;cache: 'npm'&lt;/code&gt; / &lt;code&gt;'pnpm'&lt;/code&gt; / &lt;code&gt;'yarn'&lt;/code&gt; means every job re-downloads &lt;code&gt;node_modules&lt;/code&gt;. Slow and expensive.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The interesting outlier is &lt;code&gt;axios/axios&lt;/code&gt; with &lt;strong&gt;12 error-severity findings&lt;/strong&gt;. All 12 are &lt;code&gt;deprecated-action&lt;/code&gt;: workflows still pinned to &lt;code&gt;actions/checkout@v3&lt;/code&gt;, &lt;code&gt;actions/setup-node@v3&lt;/code&gt;, and &lt;code&gt;actions/upload-artifact@v3&lt;/code&gt;. v3 of upload-artifact was deprecated in late 2024 and the v3 endpoint is being shut off. These are not "save 3% of your CI bill" findings; these are "your CI will silently start failing" findings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why these specific 3 are everywhere
&lt;/h2&gt;

&lt;p&gt;My theory: GitHub Actions doesn't push you to add any of them. The workflow files YAML-validates fine without a timeout, without concurrency, without a cache. The CI passes. The PR ships. There is no linter built in to nudge anyone toward better defaults. So the same 3 smells survive in every repo I scan, including in mine before I built ci-doctor.&lt;/p&gt;

&lt;p&gt;This is a tooling problem, not a competence problem. The maintainers of all 5 of these repos are excellent engineers. The smells are just invisible until something points at them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do about it (5-minute fixes)
&lt;/h2&gt;

&lt;p&gt;For each of the top 3 rules, here's the smallest possible change:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Add a job-level timeout
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&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;timeout-minutes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;   &lt;span class="c1"&gt;# &amp;lt;-- add this&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Add concurrency at workflow scope
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# top of every workflow that runs on PRs&lt;/span&gt;
&lt;span class="na"&gt;concurrency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workflow }}-${{ github.head_ref || github.ref }}&lt;/span&gt;
  &lt;span class="na"&gt;cancel-in-progress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Tell setup-node what package manager you use
&lt;/h3&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;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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20'&lt;/span&gt;
    &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt;      &lt;span class="c1"&gt;# or 'pnpm' or 'yarn'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or just run &lt;code&gt;npx ci-doctor --fix&lt;/code&gt; and let it patch &lt;code&gt;missing-concurrency&lt;/code&gt;, &lt;code&gt;missing-timeout&lt;/code&gt;, &lt;code&gt;wide-trigger&lt;/code&gt;, and &lt;code&gt;artifact-no-retention&lt;/code&gt; in place. The other 10 rules need a human to look at them, but those 4 are mechanical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run this scan on your own repo
&lt;/h2&gt;

&lt;p&gt;Free, browser-only, no signup: &lt;a href="https://depmedicdev-byte.github.io/scan.html" rel="noopener noreferrer"&gt;https://depmedicdev-byte.github.io/scan.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Paste any public GitHub repo URL. Get the same per-workflow report above for your own code in about 10 seconds. Shareable result URL. Nothing uploaded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Methodology
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;All 5 repos were pulled via the GitHub public API, no auth.&lt;/li&gt;
&lt;li&gt;Each &lt;code&gt;.github/workflows/*.yml&lt;/code&gt; was passed through &lt;code&gt;ci-doctor&lt;/code&gt; 0.4.1 (14 rules) and &lt;code&gt;gha-budget&lt;/code&gt; for per-job pricing.&lt;/li&gt;
&lt;li&gt;Cost = sum of all jobs at GitHub-hosted standard &lt;code&gt;ubuntu-latest&lt;/code&gt; rates, assuming 8 min/job.&lt;/li&gt;
&lt;li&gt;Monthly = per-run * 30 runs/day * 30 days. Self-hosted and large-runner jobs are unpriced.&lt;/li&gt;
&lt;li&gt;This is the same engine that runs in the browser at &lt;a href="https://depmedicdev-byte.github.io/scan.html" rel="noopener noreferrer"&gt;&lt;code&gt;/scan.html&lt;/code&gt;&lt;/a&gt;. The 20-repo version of this analysis lives at &lt;a href="https://depmedicdev-byte.github.io/benchmarks.html" rel="noopener noreferrer"&gt;&lt;code&gt;/benchmarks.html&lt;/code&gt;&lt;/a&gt; with &lt;a href="https://depmedicdev-byte.github.io/examples/" rel="noopener noreferrer"&gt;per-repo deep dives&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;blockquote&gt;
&lt;p&gt;This isn't an attack on any of these projects. They all ship excellent software, and "modeled $/mo" is not the same as "actual $/mo" - large OSS projects get free GitHub Actions credits, run things conditionally, and use self-hosted runners for the heavy lifting. The point is that the &lt;em&gt;same three workflow-YAML patterns&lt;/em&gt; show up in every repo I scan, including mine before I started building ci-doctor.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Free CLIs: &lt;a href="https://github.com/depmedicdev-byte/ci-doctor" rel="noopener noreferrer"&gt;ci-doctor&lt;/a&gt;, &lt;a href="https://github.com/depmedicdev-byte/gha-budget" rel="noopener noreferrer"&gt;gha-budget&lt;/a&gt;, &lt;a href="https://github.com/depmedicdev-byte/pin-actions" rel="noopener noreferrer"&gt;pin-actions&lt;/a&gt;. All MIT.&lt;/p&gt;

&lt;p&gt;If the in-browser report flags 5 things in your workflow and you'd rather just copy a known-good template than fix one rule at a time, the &lt;strong&gt;Cut Your CI Bill&lt;/strong&gt; cookbook ships 30 production patterns: monorepo dispatch, OIDC publish, security gates, matrix trims, the works. $19 one-time, MIT-licensed templates. &lt;a href="https://depmedicdev-byte.github.io" rel="noopener noreferrer"&gt;https://depmedicdev-byte.github.io&lt;/a&gt;&lt;/p&gt;

</description>
      <category>github</category>
      <category>devops</category>
      <category>opensource</category>
      <category>ci</category>
    </item>
    <item>
      <title>GitHub Actions linters compared - actionlint, ci-doctor, sherif, octoscan</title>
      <dc:creator>depmedicdev-byte</dc:creator>
      <pubDate>Mon, 27 Apr 2026 23:41:03 +0000</pubDate>
      <link>https://dev.to/depmedicdevbyte/github-actions-linters-compared-actionlint-ci-doctor-sherif-octoscan-3cc3</link>
      <guid>https://dev.to/depmedicdevbyte/github-actions-linters-compared-actionlint-ci-doctor-sherif-octoscan-3cc3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Disclosure: I maintain &lt;code&gt;ci-doctor&lt;/code&gt;. The comparison below describes each tool by what it documents and ships, not by my opinion of its authors. Run all four on the same workflow to see for yourself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;GitHub Actions YAML is small enough that one tool could in theory validate everything: syntax, secret hygiene, runner cost, supply-chain pinning, deprecated actions, untrusted inputs. In practice, the open-source landscape splits this work across four projects. They overlap in some areas and miss each other in others.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;actionlint&lt;/th&gt;
&lt;th&gt;ci-doctor&lt;/th&gt;
&lt;th&gt;sherif&lt;/th&gt;
&lt;th&gt;octoscan&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;YAML / shell syntax&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Untrusted input injection&lt;/td&gt;
&lt;td&gt;partial&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Action ref pinned to SHA&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Top-level &lt;code&gt;permissions&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concurrency / cancel-in-progress&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job &lt;code&gt;timeout-minutes&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache hint on setup-* actions&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Artifact retention-days&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Matrix combinatorial explosion&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost projection in dollars&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;via &lt;code&gt;gha-budget&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto-fix in place&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auto-pin actions to SHA&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;via &lt;code&gt;pin-actions&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SARIF output for Code Scanning&lt;/td&gt;
&lt;td&gt;via wrappers&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monorepo / multi-repo focus&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  actionlint
&lt;/h2&gt;

&lt;p&gt;The reference syntax checker. Written in Go, fast, no dependencies. Catches expression syntax errors, unknown event names, unknown contexts, malformed shell scripts in &lt;code&gt;run:&lt;/code&gt; blocks (it invokes &lt;code&gt;shellcheck&lt;/code&gt;), and a small number of security patterns like &lt;code&gt;${{ github.event.pull_request.title }}&lt;/code&gt; in a shell context.&lt;/p&gt;

&lt;p&gt;What it doesn't do: enforce policy. &lt;code&gt;actionlint&lt;/code&gt; tells you whether your YAML parses and runs; it does not tell you whether your workflow is cheap, secure-by-default, or maintainable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use it for:&lt;/strong&gt; pre-merge syntax validation. Catching shell quoting bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  ci-doctor
&lt;/h2&gt;

&lt;p&gt;Policy and cost focus. Eleven rules grouped into cost, security, and maintenance. Includes &lt;code&gt;--fix&lt;/code&gt; mode that auto-applies safe fixes (permissions, concurrency, timeouts, artifact retention) and &lt;code&gt;--sarif&lt;/code&gt; for GitHub Code Scanning. Pairs with &lt;a href="https://www.npmjs.com/package/pin-actions" rel="noopener noreferrer"&gt;&lt;code&gt;pin-actions&lt;/code&gt;&lt;/a&gt; for SHA pinning and &lt;a href="https://www.npmjs.com/package/gha-budget" rel="noopener noreferrer"&gt;&lt;code&gt;gha-budget&lt;/code&gt;&lt;/a&gt; for dollar cost projection.&lt;/p&gt;

&lt;p&gt;What it doesn't do: validate YAML or shell syntax (use actionlint), detect injection vulnerabilities at the expression level (use octoscan), or compare workflows across repos (use sherif).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use it for:&lt;/strong&gt; cost discipline. Default-secure policy. PR comments. Code Scanning ingestion. Auto-fixing the four common issues that have a single safe answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  sherif
&lt;/h2&gt;

&lt;p&gt;Cross-repo / monorepo lens. Sherif's value is comparing workflows across many repos under one org and surfacing inconsistency: same job, different timeout; same matrix, different runner; same checkout step, different version.&lt;/p&gt;

&lt;p&gt;What it doesn't do: ship policy rules of its own. It tells you which workflows disagree; it does not say which one is right.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use it for:&lt;/strong&gt; standardising CI across an org once you've decided on a baseline.&lt;/p&gt;

&lt;h2&gt;
  
  
  octoscan
&lt;/h2&gt;

&lt;p&gt;Security-first. Looks for untrusted-input injection patterns specifically (the &lt;code&gt;${{ github.event.* }}&lt;/code&gt; in shell context class), unpinned actions, missing top-level &lt;code&gt;permissions&lt;/code&gt;, and similar hardening misses. Emits SARIF.&lt;/p&gt;

&lt;p&gt;What it doesn't do: cost analysis, auto-fix, cache hints, retention policy, matrix sanity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use it for:&lt;/strong&gt; security audit before a release. Quick check that your &lt;code&gt;actions/checkout@...&lt;/code&gt; isn't shipping someone else's code.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to combine them
&lt;/h2&gt;

&lt;p&gt;None of these tools is a superset of the others. The cheapest composition that covers all four lenses is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# syntax&lt;/span&gt;
actionlint

&lt;span class="c"&gt;# cost + maintenance + auto-fix&lt;/span&gt;
npx ci-doctor &lt;span class="nt"&gt;--fix&lt;/span&gt;
npx ci-doctor &lt;span class="nt"&gt;--sarif&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ci-doctor.sarif

&lt;span class="c"&gt;# supply chain&lt;/span&gt;
npx pin-actions &lt;span class="nt"&gt;--check&lt;/span&gt;

&lt;span class="c"&gt;# expression-level injection scanning&lt;/span&gt;
octoscan run

&lt;span class="c"&gt;# cross-repo consistency, if you run an org&lt;/span&gt;
sherif &lt;span class="nt"&gt;--workspace&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Total runtime on a normal repo: under five seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd actually run in CI
&lt;/h2&gt;

&lt;p&gt;In order, fail fast:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;actionlint&lt;/code&gt; - syntax must be valid before policy makes sense.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npx pin-actions --check&lt;/code&gt; - cheap, supply chain.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npx ci-doctor --sarif &amp;gt; ci-doctor.sarif&lt;/code&gt; + &lt;code&gt;codeql-action/upload-sarif&lt;/code&gt; - findings as PR annotations.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;octoscan&lt;/code&gt; if your workflow accepts external input.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The first three are non-negotiable for any repo with public CI. The fourth is non-negotiable for any workflow that runs against PRs from forks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try without installing
&lt;/h2&gt;

&lt;p&gt;If you want to see what &lt;code&gt;ci-doctor&lt;/code&gt; would say about your workflow without installing anything, paste it at &lt;a href="https://depmedicdev-byte.github.io/audit.html" rel="noopener noreferrer"&gt;https://depmedicdev-byte.github.io/audit.html&lt;/a&gt;. Same engine, runs entirely in your browser, share the result by URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  See also
&lt;/h2&gt;

&lt;p&gt;I priced the workflows of 20 famous OSS projects and counted what each linter would catch: &lt;a href="https://depmedicdev-byte.github.io/benchmarks.html" rel="noopener noreferrer"&gt;https://depmedicdev-byte.github.io/benchmarks.html&lt;/a&gt; (229 workflows, 902 findings, all data linked).&lt;/p&gt;

</description>
      <category>github</category>
      <category>devops</category>
      <category>security</category>
      <category>ci</category>
    </item>
    <item>
      <title>I priced the GitHub Actions workflows of 20 famous OSS projects. The results were ugly.</title>
      <dc:creator>depmedicdev-byte</dc:creator>
      <pubDate>Mon, 27 Apr 2026 23:25:53 +0000</pubDate>
      <link>https://dev.to/depmedicdevbyte/i-priced-the-github-actions-workflows-of-20-famous-oss-projects-the-results-were-ugly-f03</link>
      <guid>https://dev.to/depmedicdevbyte/i-priced-the-github-actions-workflows-of-20-famous-oss-projects-the-results-were-ugly-f03</guid>
      <description>&lt;p&gt;There is a class of "best practices for GitHub Actions" article that just lists the same five tips - cancel concurrent runs, set a timeout, cache your dependencies, pin actions to a SHA, narrow your matrix. Every one of those articles is correct, and almost none of them tell you how often even the best open source projects miss those exact things.&lt;/p&gt;

&lt;p&gt;So I pulled the live workflow YAML out of &lt;code&gt;.github/workflows&lt;/code&gt; for 20 well-known repositories, priced every job at GitHub's published per-minute rates, and ran a linter against the whole set. The data and the tools are public.&lt;/p&gt;

&lt;p&gt;Headline numbers, all from public workflow YAML on &lt;code&gt;main&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;20 repos&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;229 workflows scanned&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;388 priced jobs&lt;/strong&gt; (159 more were jobs running on self-hosted or unknown runners and excluded from cost math)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;902 CI findings&lt;/strong&gt; flagged by &lt;code&gt;ci-doctor&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;About $51,000 of combined monthly spend&lt;/strong&gt; assuming each job runs 30 times per day at 8 minutes per run&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The interactive table is at &lt;a href="https://depmedicdev-byte.github.io/benchmarks.html" rel="noopener noreferrer"&gt;https://depmedicdev-byte.github.io/benchmarks.html&lt;/a&gt;. A few of the per-repo numbers from the dataset:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Repo&lt;/th&gt;
&lt;th&gt;$/run&lt;/th&gt;
&lt;th&gt;Monthly @ 30/day&lt;/th&gt;
&lt;th&gt;Findings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;denoland/deno&lt;/td&gt;
&lt;td&gt;$18.30&lt;/td&gt;
&lt;td&gt;$16,473.60&lt;/td&gt;
&lt;td&gt;84&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;facebook/react&lt;/td&gt;
&lt;td&gt;$16.19&lt;/td&gt;
&lt;td&gt;$14,572.80&lt;/td&gt;
&lt;td&gt;87&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vercel/next.js&lt;/td&gt;
&lt;td&gt;(varies, see table)&lt;/td&gt;
&lt;td&gt;(varies)&lt;/td&gt;
&lt;td&gt;(varies)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(The full table has all 20 repos, sorted by monthly spend.)&lt;/p&gt;

&lt;h2&gt;
  
  
  What every project gets wrong
&lt;/h2&gt;

&lt;p&gt;Across 902 findings, the distribution is heavy on a small number of issues:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;missing-timeout&lt;/td&gt;
&lt;td&gt;364&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;missing-cache&lt;/td&gt;
&lt;td&gt;113&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pinned-action-sha&lt;/td&gt;
&lt;td&gt;91&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;missing-permissions&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;artifact-no-retention&lt;/td&gt;
&lt;td&gt;73&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;matrix-overcommit&lt;/td&gt;
&lt;td&gt;52&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;missing-concurrency&lt;/td&gt;
&lt;td&gt;52&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;deprecated-action&lt;/td&gt;
&lt;td&gt;39&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fetch-depth-zero&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;expensive-runner&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wide-trigger&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;missing-timeout&lt;/code&gt; is the single biggest one. &lt;strong&gt;Out of 388 priced jobs, 364 of them are running without a &lt;code&gt;timeout-minutes&lt;/code&gt;.&lt;/strong&gt; A stuck job in CI does not exit on its own. It runs until GitHub kills it at 360 minutes, which is six hours of paid runner time, on top of whatever the job was actually trying to do.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;missing-cache&lt;/code&gt; is the second. Most projects have one cached language tool (npm, or pip, or whatever) and then forget to cache the rest. Every &lt;code&gt;pnpm install&lt;/code&gt; or &lt;code&gt;bundle install&lt;/code&gt; or &lt;code&gt;cargo build&lt;/code&gt; that runs without a cache costs you 1-3 paid minutes per job, every run.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;missing-concurrency&lt;/code&gt; only shows up 52 times but it is the highest-leverage one to fix. Without a &lt;code&gt;concurrency:&lt;/code&gt; block, a developer who pushes three quick fixes in a row triggers three full CI runs back to back. With a &lt;code&gt;concurrency:&lt;/code&gt; block, the first two cancel and only the third runs. On an active PR this can cut your monthly spend in half.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the data was generated
&lt;/h2&gt;

&lt;p&gt;Three small open-source tools, all on npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Audit a workflow:&lt;/span&gt;
npx ci-doctor .github/workflows/ci.yml

&lt;span class="c"&gt;# Estimate the cost of a workflow:&lt;/span&gt;
npx gha-budget .github/workflows/ci.yml &lt;span class="nt"&gt;--runs-per-day&lt;/span&gt; 30 &lt;span class="nt"&gt;--minutes&lt;/span&gt; 8

&lt;span class="c"&gt;# Pin every action in a workflow to a SHA:&lt;/span&gt;
npx pin-actions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The benchmark fetched workflow YAML for each repo via the GitHub REST API, then ran &lt;code&gt;ci-doctor&lt;/code&gt; and &lt;code&gt;gha-budget&lt;/code&gt; against every file. Each priced job was multiplied by 30 runs/day and 30 days/month. Self-hosted runners and &lt;code&gt;runs-on&lt;/code&gt; values not in the GitHub price sheet were excluded from cost math but still scanned for findings. Code is in &lt;a href="https://github.com/depmedicdev-byte" rel="noopener noreferrer"&gt;https://github.com/depmedicdev-byte&lt;/a&gt; if you want to reproduce it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do today
&lt;/h2&gt;

&lt;p&gt;If you have a workflow you suspect is expensive, the fastest check is to paste the YAML into one of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://depmedicdev-byte.github.io/audit.html" rel="noopener noreferrer"&gt;https://depmedicdev-byte.github.io/audit.html&lt;/a&gt; - paste, get findings, no install&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://depmedicdev-byte.github.io/budget.html" rel="noopener noreferrer"&gt;https://depmedicdev-byte.github.io/budget.html&lt;/a&gt; - paste, get $/run and monthly projection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both run entirely in your browser. They share findings and totals via URL hash so you can send a teammate a link.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd actually do
&lt;/h2&gt;

&lt;p&gt;If I had to fix one thing per repo for maximum dollar impact, the order would be:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add &lt;code&gt;concurrency: { group: ${{ github.workflow }}-${{ github.ref }}, cancel-in-progress: true }&lt;/code&gt; to every PR-triggered workflow.&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;timeout-minutes: 15&lt;/code&gt; (or whatever your real p99 is) to every job.&lt;/li&gt;
&lt;li&gt;Cache whatever your install step is downloading. &lt;code&gt;actions/cache&lt;/code&gt; for ad-hoc dirs, the official setup actions for language tools.&lt;/li&gt;
&lt;li&gt;Pin every &lt;code&gt;uses:&lt;/code&gt; to a full commit SHA. This is a security move more than a cost move, but the same &lt;code&gt;pin-actions&lt;/code&gt; CLI handles it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Those four changes alone, applied across the 20 projects in the dataset, would cut the combined monthly spend roughly in half. The longer set of patterns (60+) is in the &lt;a href="https://buy.polar.sh/polar_cl_E2HGFeAVxJ64gU0Tv0qGwAueuxvhuq6A0pjhE4BWTyD" rel="noopener noreferrer"&gt;Cut Your CI Bill cookbook&lt;/a&gt; but you do not need it to fix the top three.&lt;/p&gt;

&lt;h2&gt;
  
  
  Re-runs and updates
&lt;/h2&gt;

&lt;p&gt;The dataset will be re-run monthly. If you want a specific repo added, open an issue on any of the tool repos and I will include it.&lt;/p&gt;

</description>
      <category>github</category>
      <category>devops</category>
      <category>opensource</category>
      <category>ci</category>
    </item>
  </channel>
</rss>
