I built a tool that turns any GitHub repo into a tech-stack card - Discover Stack Fingerprint
DEV Community

I built a tool that turns any GitHub repo into a tech-stack card - Discover Stack Fingerprint

Every README has the same section: a wall of hand-picked shields.io badges, half of them out of date. The project moved from Webpack to Vite two years ago, but the badge is still there. I wanted the opposite: point a tool at a repo and let the repo tell you what it's built with. That's Stack Fingerprint. That card above is generated from the Stack Fingerprint repo itself. Nobody typed "Next.js" or "Tailwind" anywhere. How to use it in 10 seconds Paste this into your README, replacing OWNER/REPO : Stack Fingerprint That's it. No token, no sign-up, no config file. If you'd rather tweak it visually, the web builder gives you a live preview with 10+ themes, 10 layouts (classic , terminal , banner , cards …), four sizes and different icon styles, then copies the Markdown or HTML snippet for you. How detection works The scanner makes one call to the GitHub Git Trees API with recursive=1 , which returns the entire file tree flattened. No crawling directory by directory, so a scan costs a single request no matter how deep the repo is. From there it runs two passes: 1. File signals. Each technology is a small object with a check function that runs against file paths: { id: "astro", label: "Astro", color: "#FF5D01", textColor: "#ffffff", iconSlug: "astro", category: "framework", check: (f) => /^astro.config/.test(f), } astro.config.mjs exists? It's an Astro project. Dockerfile ? Docker. *.tf ? Terraform. 2. Manifest signals. Lots of libraries don't have a config file (think zod or @supabase/supabase-js ), so the scanner also reads package.json , pyproject.toml , Gemfile and composer.json and maps dependencies to signals: const DEP_SIGNALS = { next: "nextjs", "@sveltejs/kit": "sveltekit", "@prisma/client": "prisma", "drizzle-orm": "drizzle", vitest: "vitest", // ... }; It also remembers where each dependency came from. Anything that only shows up in devDependencies gets tagged as dev-only, and that turned out to matter a lot (more on that below). Right now there are 200+ signals across languages, frameworks, UI libraries, databases, testing, build tools and infrastructure. The bug that taught me how GitHub serves images The first version looked perfect on the website and broken on GitHub. Every icon was missing. The SVG used for each logo. Problem: GitHub doesn't serve README images directly. It proxies them through Camo, which serves them with a strict Content Security Policy (img-src data: ). An SVG loaded through Camo can't fetch any external resource, so every icon was blocked. The fix: resolve icons server-side from the simple-icons npm package and inline each one as a base64 data: URI inside the SVG. The card is now a single self-contained file with zero external requests, which also makes it faster and makes caching straightforward. If you're ever building a dynamic README image, keep this in mind: the SVG must be fully self-contained. Listening to feedback: "too many badges" After the first launch, the feedback was clear and fair: - Some detected technologies were unused, false positives, or not worth showing. - In monorepos, a full scan picked up signals from unrelated sub-projects. - Long lists of pills put contributors off and gave a misleading picture of the real stack. - An image from a third-party domain in your README is a supply-chain risk. Each of these became a feature. Filtering. categoryFilter decides what the card shows: | Value | Shows | |---|---| all | Everything, with dev-only signals dimmed | prodonly | Only production dependencies | top | Only languages + frameworks, max 5: the minimal badge | Monorepos. ?path=apps/web scans a single sub-directory. Config file. Drop a .stackfingerprint.json in your repo for full control: { "ignore": ["babel", "webpack"], "pin": ["nextjs", "typescript"], "labels": { "nextjs": "Next.js 14" }, "path": "apps/web" } Supply-chain safety. This was the most important one. Embedding an image from a third-party domain in a popular README means trusting whoever owns that domain. So I built a GitHub Action that generates the SVG on GitHub's runners and commits it to your repo: name: Stack Fingerprint on: push: branches: [main] schedule: - cron: "0 4 * * 1" # weekly refresh workflow_dispatch: permissions: contents: write jobs: card: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: mattqdev/stackfingerprint-action@v1 with: theme: scanner layout: classic Your README then points to a local file (./assets/stack-fingerprint.svg ), served by GitHub. No runtime dependency on my server, and it refreshes on every push. If you don't want to trust a third-party Action either, the repo has a plain curl workflow you can audit line by line, or you can deploy your own instance on Vercel in one click. The stack Fittingly, here's what it's built with: - Next.js (App Router) for the builder UI and the /api/card route - A hand-written SVG builder, no canvas or headless browser, just string templates - simple-icons for logos, inlined at render time - Tailwind CSS for the site - Supabase for the showcase of projects using the card - Vercel for hosting, with Cache-Control +stale-while-revalidate to keep GitHub's rate limits happy ๐ŸŽƒ Hacktoberfest: come contribute Stack Fingerprint is taking part in Hacktoberfest, and it's a really friendly place for a first PR. Most good first issue s are self-contained: - Add a technology: one object in src/data/signals.js (plus one line in the dependency map if needed) - Add a theme: one entry in src/data/themes.js Each issue tells you exactly which files to touch. Is your favorite framework missing from the card? That's your PR. ๐Ÿ˜‰ ๐Ÿ‘‰ Try it: stackfingerprint.vercel.app โญ Repo: github.com/mattqdev/stackfingerprint โš™๏ธ Action: GitHub Marketplace Using the card in your README? Open a showcase request and I'll feature your project on the site. What would you want it to detect next? Tell me in the comments ๐Ÿ‘‡ Top comments (0)

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.