Auto-sync your GitHub repositories to a Ghost CMS portfolio page.
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
# npm (global)
npm install -g ghost-github-portfolio
# npx (no install)
npx ghost-github-portfolio
# Docker
docker pull drumsergio/ghost-github-portfolio:0.3.0ghost-github-portfolio initThis generates a config.yml with all available options. Edit it with your GitHub username and Ghost API credentials.
- Go to your Ghost Admin panel > Settings > Integrations
- Create a new Custom Integration
- Copy the Admin API Key (format:
KEY_ID:SECRET)
# 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 --verbosegithub:
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| 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 run --rm \
-v /path/to/config.yml:/config/config.yml \
drumsergio/ghost-github-portfolio:0.3.0services:
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 policyCreate .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.
- Fetches all public repos for the configured GitHub user via the REST API
- Filters by minimum stars, excludes forks and blocklisted repos
- Sorts by star count (descending)
- Detects banner images by checking common paths (
docs/images/banner.svg,media/banner.svg, etc.) - Generates HTML cards with: banner, name, dynamic badges, description, tech stack
- Composes a Ghost lexical JSON document (the format Ghost's editor uses internally)
- 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.
| 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 |
See docs/ROADMAP.md for the full roadmap — themes, multi-CMS support, AI features, analytics, team portfolios, and more.