How I Track EOL Dates and CVEs in My README With One Badge

Published: (February 19, 2026 at 03:25 PM EST)
4 min read
Source: Dev.to

Source: Dev.to

Introduction

Every README has badges—build passing, coverage 94 %, license MIT—but none answer the question that actually matters: is this project running on supported versions?
You can have 100 % test coverage on a project pinned to Node 16 (EOL September 2025). The CI badge says “healthy,” but the runtime does not.

I wanted a badge that shows version health—whether the thing you depend on is still alive. So I built one.

Python Health
Live badge showing the current Python health grade. It auto‑updates, requires no tokens, config, or API keys—just an image URL.

What you need

BadgeMarkdown
Overall health grade![](https://img.releaserun.com/badge/health/kubernetes.svg)
EOL countdown![](https://img.releaserun.com/badge/eol/nodejs/20.svg)
Known CVEs![](https://img.releaserun.com/badge/cve/kubernetes/1.34.svg)
Latest version![](https://img.releaserun.com/badge/v/go.svg)

How health grades work

Health grades run A through F. The scoring is:

  • 35 % freshness – how current the version is
  • 35 % security – known CVEs
  • 30 % EOL status – support timeline

A critical unfixed CVE caps the grade at D. Anything past EOL for ≥ 1 year forces an F.

URL pattern

https://img.releaserun.com/badge/{type}/{product}[/{version}].svg

300 + products are supported—everything listed on endoflife.date: Python, Node.js, Go, Rust, Kubernetes, Docker, PostgreSQL, React, TypeScript, Ruby, PHP, .NET, and virtually every runtime and infrastructure tool in a production stack.

Scenario that prompted this

While reviewing a PR, I saw a green CI, 87 % coverage, active contributors—everything looked great. Then I checked the Dockerfile:

FROM node:16-alpine

Node 16 hit EOL in September 2025. No CVE scanner would flag this because the image itself isn’t vulnerable; the runtime lifecycle is the problem.

If the README had shown

![Node.js 16 EOL](https://img.releaserun.com/badge/eol/nodejs/16.svg)

anyone opening the repo would have seen the red badge immediately. Version currency is a signal, and badges make signals visible.

Adding version‑health badges to your README

## Version Health

[![Python](https://img.releaserun.com/badge/health/python/3.12.svg)](https://releaserun.com/badges/python/)
[![Node.js](https://img.releaserun.com/badge/eol/nodejs/20.svg)](https://releaserun.com/badges/nodejs/)
[![Kubernetes](https://img.releaserun.com/badge/cve/kubernetes/1.34.svg)](https://releaserun.com/badges/kubernetes/)

Wrapping the badge in a link is optional but useful—it takes readers to a landing page with full version timelines, embed snippets, and context on what the grades mean.

GitHub Action to keep badges up‑to‑date

name: Update Badges
on:
  schedule:
    - cron: '0 6 * * 1'   # Weekly Monday
  workflow_dispatch:

permissions:
  contents: write
  pull-requests: write

jobs:
  badges:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: Matheus-RR/badges@v1
        with:
          products: |
            python:3.12
            node:20
            kubernetes:1.34
          badge-types: health,eol,cve
          github-token: ${{ secrets.GITHUB_TOKEN }}

The Action scans for the / markers, updates the badge URLs, and opens a PR with the changes. Reviewing the PR lets you see what changed before merging.

Why a PR instead of a direct commit?

Badge content changes (a version goes EOL, a new CVE is published). A PR gives you a chance to verify the update before it lands in main.

Badge Builder

If you don’t want to type URLs, the Badge Builder lets you:

  • Search 300 + products
  • Pick badge types (health, EOL, CVE, latest version)
  • Preview live
  • Copy embed code in Markdown, HTML, reStructuredText, or AsciiDoc

Comparison with shields.io

  • shields.io – static labels and CI metrics (build status, coverage, npm version). Great for pipeline status.
  • ReleaseRun – live version intelligence: health grades that age, real‑time EOL countdowns, CVE counts that reflect the current threat landscape.

Use both: shields.io for CI pipeline status, ReleaseRun for dependency health.

Query parameters

All badges accept optional query parameters, e.g.:

https://img.releaserun.com/badge/health/python.svg?style=flat-square&label=runtime
ParameterWhat it does
styleflat (default) or flat-square
labelCustom left‑side text
colorOverride right‑side color
labelColorOverride left‑side color

Service architecture

The service queries endoflife.date for version lifecycle data, enriches it with CVE data from NVD, computes health grades, and renders SVG badges on the fly via pybadges.

Four cache layers keep it fast:

  1. In‑memory SVG cache (2 min) → sub‑50 ms for repeat requests
  2. Data cache (5 min) → limits upstream API calls
  3. HTTP cache headers → browser + CDN caching
  4. Cloudflare CDN → global edge

Badges are never more than 5 minutes stale.

Example badges

![](https://img.releaserun.com/badge/health/python.svg)
![](https://img.releaserun.com/badge/eol/nodejs/20.svg)
![](https://img.releaserun.com/badge/cve/kubernetes/1.34.svg)
![](https://img.releaserun.com/badge/v/docker-engine.svg)

Documentation & resources

  • Full docs & product list:
  • GitHub Action:
  • Badge Builder:
  • ReleaseRun tracks software releases across 300 + technologies. The badge service is free.
0 views
Back to Blog

Related posts

Read more »

Docs that never lie

!Lionel Draghihttps://media2.dev.to/dynamic/image/width=50,height=50,fit=cover,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%...

Apex B. OpenClaw, Local Embeddings.

Local Embeddings para Private Memory Search Por default, el memory search de OpenClaw envía texto a un embedding API externo típicamente Anthropic u OpenAI par...