endoflife-date/endoflife.date
Informative site with EoL dates of everything
Healthy across the board
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit today
- ✓25+ active contributors
- ✓Distributed ownership (top contributor 26% of recent commits)
Show 3 more →Show less
- ✓MIT licensed
- ✓CI configured
- ⚠No test directory detected
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/endoflife-date/endoflife.date)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/endoflife-date/endoflife.date on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: endoflife-date/endoflife.date
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/endoflife-date/endoflife.date shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit today
- 25+ active contributors
- Distributed ownership (top contributor 26% of recent commits)
- MIT licensed
- CI configured
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live endoflife-date/endoflife.date
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/endoflife-date/endoflife.date.
What it runs against: a local clone of endoflife-date/endoflife.date — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in endoflife-date/endoflife.date | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 30 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of endoflife-date/endoflife.date. If you don't
# have one yet, run these first:
#
# git clone https://github.com/endoflife-date/endoflife.date.git
# cd endoflife.date
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of endoflife-date/endoflife.date and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "endoflife-date/endoflife.date(\\.git)?\\b" \\
&& ok "origin remote is endoflife-date/endoflife.date" \\
|| miss "origin remote is not endoflife-date/endoflife.date (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "_config.yml" \\
&& ok "_config.yml" \\
|| miss "missing critical file: _config.yml"
test -f "_plugins/end-of-life.rb" \\
&& ok "_plugins/end-of-life.rb" \\
|| miss "missing critical file: _plugins/end-of-life.rb"
test -f "_plugins/product-data-validator.rb" \\
&& ok "_plugins/product-data-validator.rb" \\
|| miss "missing critical file: _plugins/product-data-validator.rb"
test -f "product-schema.json" \\
&& ok "product-schema.json" \\
|| miss "missing critical file: product-schema.json"
test -f "_layouts/product.html" \\
&& ok "_layouts/product.html" \\
|| miss "missing critical file: _layouts/product.html"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 30 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~0d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/endoflife-date/endoflife.date"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
endoflife.date is a Jekyll-powered static website that aggregates and presents End-of-Life (EoL) dates and support lifecycles for software products, infrastructure, and tools. It serves as a searchable, reference database with a REST API (in Beta) and iCalendar exports, solving the problem that EoL information is historically scattered, inconsistent, or poorly documented across vendors. Jekyll monolith: _data/ contains YAML/JSON product definitions, _layouts/ holds product-list.html and product.html for rendering, _plugins/ contains Ruby generators (create-icalendar-files.rb for iCal exports), _includes/ houses reusable template components (table.html, product-icon.html), and top-level markdown files become pages. Client-side search uses Lunr (custom-index.js in _includes/lunr/).
👥Who it's for
DevOps engineers, SREs, and platform teams who need to track dependency lifecycles and plan upgrade schedules; maintainers of open-source projects who want to publish official EoL dates; and contributors who help curate and update product lifecycle data.
🌱Maturity & risk
Production-ready and actively maintained. The project is deployed to Netlify with passing CI/CD (lint, link-checking, auto-merge for release updates). It has solid scaffolding (HACKING.md, CONTRIBUTING.md, CODE-OF-CONDUCT.md), semantic versioning (CHANGELOG_API.md), and a working API in Beta. Regular GitHub Actions workflows and the presence of Dependabot suggest active oversight.
Low risk overall due to static site generation (no runtime complexity), but risks include: reliance on community contributions for data accuracy with no obvious validation layer (data lives in _data/ but no schema validation visible), single Jekyll version lock in Gemfile could cause build issues if dependencies age, and the Beta API may break between releases. Open source sustainability depends on maintainer time.
Active areas of work
Active development visible through GitHub Actions workflows for CI/CD, auto-merge of release updates, link-checking automation, and linting. Recent work includes Dependabot for dependency updates and Copilot integration guidelines (.github/copilot-instructions.md). The API is in Beta refinement with documentation at CHANGELOG_API.md.
🚀Get running
git clone https://github.com/endoflife-date/endoflife.date.git && cd endoflife.date && bundle install && bundle exec jekyll serve --livereload. The site will be available at http://localhost:4000.
Daily commands: bundle exec jekyll serve --livereload (runs on localhost:4000 with live reload for development). For production build: bundle exec jekyll build (outputs to _site/). Tests/linting: GitHub Actions run lint (.github/workflows/lint.yml) and link-checking (.github/workflows/check-links.yml) automatically.
🗺️Map of the codebase
_config.yml— Jekyll configuration defining site structure, plugins, collections, and build settings that every contributor must understand_plugins/end-of-life.rb— Core Jekyll plugin implementing product parsing and EoL date logic that powers the entire site_plugins/product-data-validator.rb— Validation plugin ensuring product data integrity; blocks builds with invalid EoL dates or missing required fieldsproduct-schema.json— JSON schema defining the required structure for all product data files; essential reference for adding new products_layouts/product.html— Primary product detail page template rendering EoL calendars, release tables, and metadata for each product_plugins/generate-api-v1.rb— Generates the public REST API from product data; critical for all API consumers and integrationsCONTRIBUTING.md— Contribution guidelines documenting pull request process, product addition checklist, and data format expectations
🛠️How to make changes
Add a New Product
- Create a new Markdown file in the products directory (e.g., products/example-product.md) with YAML front matter following the schema (
product-schema.json) - Define product metadata (title, category, link) and releases array with cycle, releaseDate, eol, lts, and support fields (
products/[product].md) - Run validation locally using the lint script to ensure data conforms to schema before pushing (
bin/lint-product.sh) - Submit PR with product file; CI workflows (lint.yml, check-links.yml) will automatically validate on GitHub (
.github/workflows/lint.yml) - Upon merge, Jekyll plugins (_plugins/end-of-life.rb, product-data-validator.rb) parse the product and generate API endpoints and HTML pages automatically (
_plugins/end-of-life.rb)
Add a New API Endpoint
- Review the v1 API generation logic in generate-api-v1.rb to understand how product data is transformed into JSON endpoints (
_plugins/generate-api-v1.rb) - Add new endpoint definition within the generate-api-v1.rb plugin, specifying URL pattern and data projection logic (
_plugins/generate-api-v1.rb) - Update the OpenAPI specification (api_v1/openapi.yml) to document the new endpoint with request/response schemas (
api_v1/openapi.yml) - Build locally and test the endpoint at /_/api/v1/[endpoint].json to verify output (
api_v1/openapi.yml)
Customize Product Page Display
- Examine _layouts/product.html to understand the template structure for product detail pages, including tables, calendars, and metadata sections (
_layouts/product.html) - Use Liquid filters from _plugins/end-of-life-filters.rb to format dates, calculate status, and generate display logic within the template (
_plugins/end-of-life-filters.rb) - Create custom HTML includes in _includes/ for new product sections and reference them in product.html (
_includes/table.html) - Add styles to _sass/custom/custom.scss to implement new layout or visual elements (
_sass/custom/custom.scss)
Generate New Data Export Format (Feed/Calendar)
- Review existing feed generators (_plugins/generate-product-feeds.rb, create-icalendar-files.rb) to understand the pattern (
_plugins/generate-product-feeds.rb) - Create a new plugin in _plugins/ (e.g., generate-custom-export.rb) that inherits Jekyll::Generator and hooks into the site build (
_plugins/generate-product-feeds.rb) - Implement the generate() method to iterate products and write output files in your desired format (
_plugins/generate-product-feeds.rb) - Rebuild the site; your new export files will be generated alongside products and published to the Netlify site (
netlify.toml)
🔧Why these technologies
- Jekyll — Static site generator optimized for content-heavy documentation; Markdown-first workflow ideal for community contributions of product data
- Ruby plugins — Jekyll plugin ecosystem provides native hooks for parsing YAML product files, validating schemas, and generating API endpoints at build time
- Liquid templating — Jekyll's template language allows reusable product layouts with custom filters for date math and EoL calculations
- Netlify hosting + CDN — Automatic deployments from Git, global edge caching, and built-in redirects (_redirects file) for SEO-friendly product URLs
- Lunr.js client-side search — No server required; static search index embedded in HTML enables full-text product discovery without backend dependencies
⚖️Trade-offs already made
-
Build-time API generation instead of dynamic API server
- Why: Reduces operational overhead and infrastructure cost; all endpoints are pre-computed static JSON files
- Consequence: API updates require site rebuild (~5min); cannot respond to real-time requests without rebuilding
-
YAML product files checked into Git instead of external database
- Why: Leverages Git history for auditing EoL date changes; enables community contributions via pull requests without auth layer
- Consequence: Scales linearly with product count; searching 500+ product files during build increases build time; no soft-delete capability
-
Client-side Lunr search instead of server-side full-text search
- Why: Zero backend operational cost; static index pre-built and shipped with HTML
- Consequence: Search index size grows with product count; limited advanced query syntax compared to Elasticsearch
-
Atom feeds and iCalendar exports generated at build time
- Why: Subscribers receive feeds from static CDN; no polling of a live API required
- Consequence: Feed freshness bounded by build cycle (~every 15min on PR merge); subscribers see updates with slight delay
🚫Non-goals (don't propose these)
- Real-time product data updates without rebuild
- User authentication or personalized views
🪤Traps & gotchas
No obvious hidden traps, but be aware: (1) Product data lives in _data/ as structured YAML/JSON — there's no visible schema validation, so data quality depends on reviewer rigor. (2) The iCalendar export plugin (_plugins/create-icalendar-files.rb) runs at build time and generates files; if the plugin breaks, builds fail silently unless you run jekyll build locally. (3) Lunr search index is built at site generation time (_includes/lunr/custom-index.js); changes to product frontmatter won't be searchable until a rebuild. (4) The API is Beta (CHANGELOG_API.md notes breaking changes possible); pinning to specific API versions in CI pipelines is recommended.
🏗️Architecture
💡Concepts to learn
- Jekyll plugins (Ruby generators) — The iCalendar export feature (_plugins/create-icalendar-files.rb) uses Jekyll's plugin API to generate files during build; understanding this pattern is essential for extending endoflife.date's capabilities
- Liquid templating — SCSS and HTML files use Liquid syntax (e.g., _includes/css/activation.scss.liquid) to inject dynamic data; you must understand Liquid conditionals and loops to modify templates
- Full-text search with Lunr.js — endoflife.date implements client-side search without a database backend; Lunr enables offline indexing and query parsing, critical for the UX and performance
- iCalendar (RFC 5545) — The Ruby plugin generates .ics files for EoL dates; understanding the iCalendar format helps troubleshoot export issues and extend calendar integrations
- OpenAPI / Swagger specification — The API (_layouts/swagger-ui.html) is documented via OpenAPI; contributors working on API changes must understand this spec format for documentation accuracy
- Static site generation build pipelines — Jekyll builds a static site with no runtime server; understanding the build→deploy cycle (GitHub Actions, Netlify) is essential for PR validation and production releases
- YAML frontmatter in markdown — Every product page uses YAML frontmatter to define EoL cycles, support dates, and metadata; correct formatting is essential for data integrity and template rendering
🔗Related repos
facebook/react— Major technology frequently referenced on endoflife.date; many users track React's EoL to understand their own project timelinesnodejs/node— Node.js is one of the most queried products on endoflife.date; users sync their Node version strategy with official LTS datespython/cpython— Python version EoL tracking is a core use case; endoflife.date aggregates and presents CPython's official PEP 619 schedulejust-the-docs/just-the-docs— Jekyll theme used for documentation site layout and styling (referenced in README and visible in _layouts/)MkDocs/mkdocs— Alternative static documentation generator for similar use cases (API docs, product timelines); helps understand Jekyll's role in this project
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add integration tests for product data validation plugin
The repo has _plugins/product-data-validator.rb which validates product data during build, but there's no visible test suite for it. Adding tests would catch data quality issues early (malformed dates, missing required fields, inconsistent schema) before they make it into production. This is critical since the entire site's value proposition depends on accurate EoL data.
- [ ] Create
spec/plugins/product-data-validator_spec.rbwith RSpec tests - [ ] Test validation of required fields in product YAML files
- [ ] Test date format validation and future-date constraints
- [ ] Test the validator against sample malformed product files in
_data/directory - [ ] Add
spec_helper.rband updateGemfilewithrspecdependency - [ ] Add GitHub Actions workflow
.github/workflows/test-plugins.ymlto run specs on PR
Add automated API schema validation against OpenAPI spec
The repo exposes both api_v0 and api_v1 endpoints (via _plugins/generate-api-v*.rb) and maintains api_v1/openapi.yml. However, there's no CI check ensuring generated API responses actually match the documented OpenAPI schema. This prevents breaking API changes from being deployed silently.
- [ ] Add a GitHub Actions workflow
.github/workflows/validate-api-schema.yml - [ ] Use
openapi-spec-validatoror similar to lint the OpenAPI spec on changes toapi_v1/openapi.yml - [ ] Generate sample API responses from the build output and validate them against the schema using a tool like
swagger-cli - [ ] Add the validation step to
.github/workflows/lint.ymlor create a dedicated workflow - [ ] Document the API contract validation in
HACKING.md
Add unit tests for Jekyll plugin filters in end-of-life-filters.rb
The file _plugins/end-of-life-filters.rb contains custom Liquid filters used throughout product templates (likely referenced in _layouts/product.html and _includes/table.html). These filters transform dates and lifecycle data but appear untested. Edge cases in date parsing or formatting could silently break the site's display logic.
- [ ] Create
spec/plugins/end_of_life_filters_spec.rbwith RSpec tests - [ ] Test all custom filters: date formatting, status calculations, version comparisons
- [ ] Add edge case tests (leap years, past dates, future dates, invalid inputs)
- [ ] Test filter behavior with the actual Jekyll environment using
jekyll-testor similar - [ ] Add
rspectoGemfileif not already present and update Ruby test workflow
🌿Good first issues
- Add missing product pages: many technologies lack dedicated EoL pages (e.g., lesser-known frameworks). Pick any product from your stack, research its public EoL schedule, create a markdown file in _data/ with cycles and dates, and submit a PR. Follow the structure in existing product files.
- Improve search relevance: the Lunr index (_includes/lunr/custom-index.js and _includes/lunr/custom-data.json) currently indexes basic fields. Extend custom-data.json to include product aliases or tags (e.g., 'Node.js' should also match 'NodeJS', 'Node'), then test search quality locally with bundle exec jekyll serve.
- Add validation for product YAML: there's no visible schema validation for _data/ product definitions. Write a Ruby plugin (in _plugins/) that validates required fields (name, cycles, dates, support dates) on build-time and fails with clear errors. This prevents invalid data in PRs.
⭐Top contributors
Click to expand
Top contributors
- @usta — 26 commits
- @dependabot[bot] — 19 commits
- @github-actions[bot] — 18 commits
- @marcwrobel — 5 commits
- @BiNZGi — 4 commits
📝Recent commits
Click to expand
Recent commits
6e0f0a1— 🤖: Update latest release data (github-actions[bot])ebdb13f— Bump _data/release-data fromd29d786to07b5c11(dependabot[bot])9910889— 🤖: Update latest release data (github-actions[bot])a0c1983— Bump _data/release-data from910e206tod29d786(dependabot[bot])bc10013— [wireshark] Add 4.6, update lifecycle dates (#10061) (lossos)959c847— [New Product] JRuby (#10015) (etagwerker)1d222b4— [Tailwindcss] Add release information for Tailwind CSS 4.3 (#10060) (hebbet)b1faccf— [vitess] Add cycle 24 (#10054) (usta)a510a9f— [rabbitmq] Add 4.3 cycle (#10029) (usta)35ae33b— [containerd] Add 2.3 LTS cycle and mark 2.0 cycle as LTS too (#10028) (usta)
🔒Security observations
The endoflife.date codebase demonstrates generally good security practices as a static Jekyll site. However, there are areas for improvement: missing visible security headers configuration, outdated npm dependencies without lock file management, and potential Liquid template injection risks. The use of custom Ruby plugins adds complexity that requires regular security audits. No critical vulnerabilities are evident, but implementing security headers, maintaining dependencies, and adding security contribution guidelines would significantly strengthen the security posture. This is a low-risk project given its primarily static nature, but should follow best practices for build-time security and dependency management.
- Medium · Outdated Netlify Plugin Dependency —
package.json. The package.json uses 'netlify-plugin-submit-sitemap' version ^0.4.0, which is a relatively old version. No lock file (package-lock.json or yarn.lock) is visible, making dependency resolution unpredictable and potentially allowing installation of vulnerable transitive dependencies. Fix: 1) Upgrade to the latest version of netlify-plugin-submit-sitemap. 2) Implement a lock file (package-lock.json or yarn.lock) in version control. 3) Runnpm auditoryarn auditregularly to identify known vulnerabilities. - Medium · Missing Security Headers Configuration —
_headers. While a '_headers' file exists (indicating Netlify deployment), the actual content is not provided. Without proper security headers (CSP, X-Frame-Options, X-Content-Type-Options, etc.), the site may be vulnerable to XSS, clickjacking, and MIME-type sniffing attacks. Fix: Implement comprehensive security headers including: Content-Security-Policy, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, X-XSS-Protection, Referrer-Policy, and Permissions-Policy headers. - Low · Liquid Template Injection Risk —
_includes/lunr/custom-data.json, _includes/*.html. The codebase uses Jekyll with Liquid templates (*.liquid files in _includes). While Jekyll is generally safe, dynamic content rendering in templates could pose XSS risks if user input is not properly escaped. Fix: Ensure all dynamic content in Liquid templates uses proper filters (e.g., | escape, | uri_escape). Review any custom plugins that generate dynamic content for proper output escaping. - Low · Ruby Plugin Security Considerations —
_plugins/. Multiple custom Ruby plugins are present (_plugins directory) that extend Jekyll functionality. Custom Ruby code execution during build could be a supply chain risk if plugins execute arbitrary code or process untrusted data. Fix: 1) Regularly audit custom plugins for security issues. 2) Validate and sanitize any external data processed by plugins. 3) Use Ruby security tools like Brakeman for code analysis. 4) Limit plugin capabilities to necessary operations only. - Low · Missing CONTRIBUTING.md Security Guidelines —
CONTRIBUTING.md. While CONTRIBUTING.md exists, without visible security guidelines for contributors, there's risk of malicious pull requests introducing vulnerabilities into the codebase. Fix: Add a 'Security Policy' section to CONTRIBUTING.md and create a SECURITY.md file documenting how to report security vulnerabilities responsibly. - Low · No Visible .gitignore for Sensitive Files —
.gitignore. .gitignore exists but content is not provided. Ensure it properly excludes sensitive files like .env, credentials, API keys, and build artifacts. Fix: Verify .gitignore includes: *.env, .env.local, .env..local, credentials, node_modules, vendor, and any build artifacts. Use a template from https://gitignore.io for Jekyll projects.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.