npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ghost-github-portfolio

v0.3.5

Published

Auto-sync GitHub repositories to a Ghost CMS portfolio page. Fetches repos, sorts by stars, generates cards with banners and badges, and updates Ghost via the Admin API.

Readme


What it does

ghost-github-portfolio fetches your public GitHub repositories, sorts them by stars, generates beautiful portfolio cards with banners and badges, and pushes the result to a Ghost page via the Admin API.

Run it daily via cron, GitHub Actions, or Docker to keep your portfolio always up to date.

Features:

  • Fetches repos sorted by stars, filtered by configurable minimum
  • Auto-detects banner images (docs/images/banner.svg, media/banner.svg, etc.)
  • Dynamic shields.io badges (stars, forks, license, Docker pulls, website, awesome-list)
  • Per-repo overrides (custom description, badges, Docker image, tech stack)
  • Ghost lexical editor format (the current Ghost editor)
  • Dry-run mode for previewing changes
  • Runs as CLI, Docker container, or GitHub Action

Quick start

1. Install

# npm (global)
npm install -g ghost-github-portfolio

# npx (no install)
npx ghost-github-portfolio

# Docker
docker pull drumsergio/ghost-github-portfolio:0.3.0

2. Create a config

ghost-github-portfolio init

This generates a config.yml with all available options. Edit it with your GitHub username and Ghost API credentials.

3. Get your Ghost Admin API key

  1. Go to your Ghost Admin panel > Settings > Integrations
  2. Create a new Custom Integration
  3. Copy the Admin API Key (format: KEY_ID:SECRET)

4. Run

# Preview (no changes to Ghost)
ghost-github-portfolio sync --config config.yml --dry-run --verbose

# Sync to Ghost
ghost-github-portfolio sync --config config.yml --verbose

Configuration

github:
  username: YOUR_GITHUB_USERNAME
  # token: ghp_xxx  # Optional: higher rate limits (env: GHOST_GITHUB_TOKEN)

ghost:
  url: https://your-ghost-blog.com
  adminApiKey: "KEY_ID:SECRET_HEX"
  pageSlug: portfolio   # or pageId: "hex_id"

portfolio:
  minStars: 2
  maxRepos: 50
  includeForked: false
  badgeStyle: for-the-badge  # flat, flat-square, for-the-badge, plastic, social
  showBanner: true
  centerContent: true
  defaultBannerPath: docs/images/banner.svg

  bannerPaths:
    awesome-spain: media/banner.svg  # Override per repo

  excludeRepos:
    - .github

  repos:
    my-project:
      description: "Custom description"
      dockerImage: myuser/my-project  # Adds Docker pulls badge
      techStack: "Python, Docker, Redis"
      badges:
        - type: website
          url: https://my-project.com
        - type: awesome-list
        - type: platform
          label: macOS
          logo: apple
        - type: docs
          url: https://docs.my-project.com
        - type: custom
          label: MCP
          value: Official Registry
          color: E6522C

  footer:
    showStats: true
    showViewAll: true

Environment variables

| Variable | Description | |----------|-------------| | GHOST_GITHUB_TOKEN | GitHub token for private repos / higher rate limits | | GHOST_ADMIN_API_KEY | Ghost Admin API key (overrides config file) |

Docker

docker run --rm \
  -v /path/to/config.yml:/config/config.yml \
  drumsergio/ghost-github-portfolio:0.3.0

Docker Compose (daily cron)

services:
  ghost-portfolio:
    image: drumsergio/ghost-github-portfolio:0.3.0
    volumes:
      - ./config.yml:/config/config.yml:ro
    # Run daily at 6 AM via external cron or restart policy

GitHub Action

Create .github/workflows/portfolio.yml in any repo:

name: Update Portfolio

on:
  schedule:
    - cron: "0 6 * * *"  # Daily at 6 AM UTC
  workflow_dispatch:       # Manual trigger

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22

      - run: npx ghost-github-portfolio sync --config config.yml --verbose
        env:
          GHOST_GITHUB_TOKEN: ${{ secrets.GHOST_GITHUB_TOKEN }}

Store config.yml in the repo (without secrets) and use environment variables for the API keys.

How it works

  1. Fetches all public repos for the configured GitHub user via the REST API
  2. Filters by minimum stars, excludes forks and blocklisted repos
  3. Sorts by star count (descending)
  4. Detects banner images by checking common paths (docs/images/banner.svg, media/banner.svg, etc.)
  5. Generates HTML cards with: banner, name, dynamic badges, description, tech stack
  6. Composes a Ghost lexical JSON document (the format Ghost's editor uses internally)
  7. Updates the target Ghost page via the Admin API with JWT authentication

Badges are dynamic (served by shields.io) — stars, forks, and Docker pulls update automatically on every page view without re-running the tool.

Badge types

| Type | Auto-detected | Description | |------|--------------|-------------| | Stars | Yes | Always shown | | Forks | Yes | Always shown | | License | Yes | From GitHub repo metadata | | Docker pulls | Config | Set dockerImage in repo overrides | | Website | Yes | From GitHub homepage field | | Awesome list | Yes | If repo has awesome-list topic | | Docs | Yes | If homepage is a GitHub Pages URL | | Platform | Config | Custom platform badge (macOS, Linux, etc.) | | Custom | Config | Any shields.io-compatible badge |

Roadmap

See docs/ROADMAP.md for the full roadmap — themes, multi-CMS support, AI features, analytics, team portfolios, and more.

License

GPL-3.0