Skip to content
/ ssg Public

A simple static site generator written in Go. Converts content from WordPress exports (Markdown format with YAML frontmatter) to static HTML, CSS, and JS files.

License

BSD-3-Clause, BSD-3-Clause licenses found

Licenses found

BSD-3-Clause
LICENSE
BSD-3-Clause
LICENSE.md
Notifications You must be signed in to change notification settings

spagu/ssg

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

59 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

SSG - Static Site Generator

Go Version Go Report Card License CI codecov GitHub Action GitHub issues GitHub stars GitHub Release Docker GitHub forks OpenSSF Scorecard

A fast and flexible static site generator built in Go, designed for simplicity and speed.

Website | Installation | Documentation | Contributing | Security


πŸ” Overview

SSG is a static site generator written in Go, optimized for converting WordPress exports (Markdown with YAML frontmatter) to blazing-fast static websites. With its simple architecture, multiple template engine support, and powerful asset pipelines, SSG renders a complete site in milliseconds.

What Can You Build?

SSG is perfect for creating:

  • οΏ½ Blogs - Personal or professional blogs migrated from WordPress
  • 🏒 Corporate sites - Fast, secure company websites
  • πŸ“š Documentation - Technical docs with clean SEO URLs
  • 🎨 Portfolios - Image galleries and creative showcases
  • πŸ“„ Landing pages - Marketing and product pages
  • πŸ“ Personal sites - Resumes, CVs, and personal branding

Key Capabilities

Feature Description
⚑ Lightning Fast Go-powered generation completes in milliseconds
🎭 Multiple Engines Go templates, Pongo2 (Jinja2), Mustache, Handlebars
🌐 Hugo Themes Download and use Hugo themes from GitHub
πŸ–ΌοΈ Image Pipeline WebP conversion with quality control
πŸ“¦ Asset Bundling HTML, CSS, JS minification
πŸ”„ Live Reload Built-in server with file watching
🐳 Docker Ready Minimal Alpine image (~15MB)
🎬 CI/CD Native First-class GitHub Actions support

Development Workflow

Use SSG's embedded web server during development to instantly see changes to content, structure, and presentation. The watch mode automatically rebuilds your site when files change:

# Start development server with auto-rebuild
ssg my-content krowy example.com --http --watch

Then deploy to any static hosting:

  • Cloudflare Pages - Zero-config with our example workflow
  • GitHub Pages - Direct push deployment
  • Netlify, Vercel - Drag and drop or Git integration
  • Any web server - Just copy the output folder

Asset Processing

SSG includes powerful asset processing:

  • Image Processing - Convert JPG/PNG to WebP with configurable quality
  • HTML Minification - Remove whitespace, comments, optimize output
  • CSS Minification - Bundle and compress stylesheets
  • JS Minification - Optimize JavaScript files
  • SEO Automation - Sitemap, robots.txt, clean URLs, meta tags

✨ Features

Core Features

  • πŸš€ Fast generation - Go-powered, millisecond builds
  • πŸ“ Markdown - Full support with YAML frontmatter
  • 🎨 Built-in templates - simple (dark) and krowy (green/natural)
  • πŸ“± Responsive - Mobile-first design
  • β™Ώ Accessible - WCAG 2.2 compliant
  • πŸ” SEO - Clean URLs, sitemap, robots.txt

Template Engines

  • πŸ”§ Go Templates - Default, powerful templating (.Variable)
  • 🐍 Pongo2 - Jinja2/Django syntax (for loops, filters)
  • πŸ‘¨β€πŸ¦± Mustache - Logic-less templates (sections)
  • πŸ”¨ Handlebars - Semantic templates (each blocks)

Development

  • 🌐 HTTP Server - Built-in dev server (--http)
  • πŸ‘€ Watch Mode - Auto-rebuild on changes (--watch)
  • πŸ“„ Config Files - YAML, TOML, JSON support
  • 🧹 Clean Builds - Fresh output (--clean)

Production

  • πŸ–ΌοΈ WebP Conversion - Optimized images (--webp)
  • πŸ—„οΈ Minification - HTML, CSS, JS (--minify-all)
  • πŸ“¦ Deployment Package - Cloudflare Pages ready (--zip)
  • 🐳 Docker - Multi-arch Alpine image

Integration

  • 🎬 GitHub Actions - Use as CI/CD step
  • 🌍 Online Themes - Download Hugo themes from URL
  • πŸ“ WordPress - Import from WP exports

πŸ“¦ Requirements

  • Go 1.25 or later
  • Make (optional, for Makefile)
  • cwebp (optional, for WebP conversion)

πŸš€ Installation

Quick Install (Linux/macOS)

curl -sSL https://raw.githubusercontent.com/spagu/ssg/main/install.sh | bash

Package Managers

Platform Command
Homebrew (macOS/Linux) brew install spagu/tap/ssg
Snap (Ubuntu) snap install ssg
Debian/Ubuntu wget https://github.com/spagu/ssg/releases/download/v1.3.0/ssg_1.3.0_amd64.deb && sudo dpkg -i ssg_1.3.0_amd64.deb
Fedora/RHEL sudo dnf install https://github.com/spagu/ssg/releases/download/v1.3.0/ssg-1.3.0-1.x86_64.rpm
FreeBSD pkg install ssg or from ports
OpenBSD From ports: /usr/ports/www/ssg

Binary Downloads

Download pre-built binaries from GitHub Releases:

Platform AMD64 ARM64
Linux ssg-linux-amd64.tar.gz ssg-linux-arm64.tar.gz
macOS ssg-darwin-amd64.tar.gz ssg-darwin-arm64.tar.gz
FreeBSD ssg-freebsd-amd64.tar.gz ssg-freebsd-arm64.tar.gz
Windows ssg-windows-amd64.zip ssg-windows-arm64.zip

From Source

git clone https://github.com/spagu/ssg.git
cd ssg
make build
sudo make install

Docker

# Pull image from GitHub Container Registry
docker pull ghcr.io/spagu/ssg:latest

# Run SSG in container
docker run --rm -v $(pwd):/site ghcr.io/spagu/ssg:latest \
    my-content krowy example.com --webp

# Or use docker-compose
docker compose run --rm ssg my-content krowy example.com

# Development server with watch mode
docker compose up dev

πŸ“– Full installation guide: docs/INSTALL.md

πŸ’» Usage

Syntax

ssg <source> <template> <domain> [options]

Arguments

Argument Description
source Source folder name (inside content-dir)
template Template name (inside templates-dir)
domain Target domain for the generated site

Configuration File

SSG supports configuration files in YAML, TOML, or JSON format. Auto-detects: .ssg.yaml, .ssg.toml, .ssg.json

# Use explicit config file
ssg --config .ssg.yaml

# Or just create .ssg.yaml and run ssg (auto-detected)
ssg

Example .ssg.yaml:

source: "my-content"
template: "krowy"
domain: "example.com"

http: true
watch: true
clean: true
webp: true
webp_quality: 80
minify_all: true

See .ssg.yaml.example for all options.

Options

Configuration:

Option Description
--config=FILE Load config from YAML/TOML/JSON file

Server & Development:

Option Description
--http Start built-in HTTP server (default port: 8888)
--port=PORT HTTP server port (default: 8888)
--watch Watch for changes and rebuild automatically
--clean Clean output directory before build

Output Control:

Option Description
--sitemap-off Disable sitemap.xml generation
--robots-off Disable robots.txt generation
--pretty-html Prettify HTML (remove all blank lines)
--relative-links Convert absolute URLs to relative links
--post-url-format=FMT Post URL format: date (default: /YYYY/MM/DD/slug/) or slug (/slug/)
--minify-all Minify HTML, CSS, and JS
--minify-html Minify HTML output
--minify-css Minify CSS output
--minify-js Minify JS output
--sourcemap Include source maps in output

Image Processing (Native Go - no external tools needed):

Option Description
--webp Convert images to WebP format (requires cwebp)
--webp-quality=N WebP compression quality 1-100 (default: 60)

Deployment:

Option Description
--zip Create ZIP file for Cloudflare Pages

Paths:

Option Description
--content-dir=PATH Content directory (default: content)
--templates-dir=PATH Templates directory (default: templates)
--output-dir=PATH Output directory (default: output)

Template Engine:

Option Description
--engine=ENGINE Template engine: go (default), pongo2, mustache, handlebars
--online-theme=URL Download theme from URL (GitHub, GitLab, or direct ZIP)

Other:

Option Description
--quiet, -q Suppress output (only exit codes)
--version, -v Show version
--help, -h Show help

Shortcodes

Define reusable content snippets in your config file. Each shortcode requires a template file:

shortcodes:
  - name: "thunderpick"
    template: "shortcodes/banner.html"  # Required: template in theme folder
    title: "Thunderpick"
    text: "100% up to $1000 + 5% rakeback"
    url: "https://example.com/promo"
    logo: "/assets/images/thunderpick.png"
    legal: "18+. Gamble Responsibly. T&Cs Apply."

Create the template file (e.g., templates/your-theme/shortcodes/banner.html):

<div class="promo-banner">
  <a href="{{.URL}}">
    {{if .Logo}}<img src="{{.Logo}}" alt="{{.Name}}">{{end}}
    <strong>{{.Title}}</strong>
    <span>{{.Text}}</span>
  </a>
  {{if .Legal}}<small>{{.Legal}}</small>{{end}}
</div>

Use in markdown content with {{shortcode_name}}:

Check out this amazing offer:

{{thunderpick}}

Don't miss it!

Available template variables: {{.Name}}, {{.Title}}, {{.Text}}, {{.URL}}, {{.Logo}}, {{.Legal}}, {{.Data.key}}

Examples

# Development mode: HTTP server + auto-rebuild on changes
./build/ssg my-content krowy example.com --http --watch

# HTTP server on custom port
./build/ssg my-content krowy example.com --http --port=3000

# Generate site with krowy template
./build/ssg krowy.net.2026-01-13110345 krowy krowy.net

# Generate with simple template (dark theme)
./build/ssg krowy.net.2026-01-13110345 simple krowy.net

# Generate with WebP conversion and ZIP package
./build/ssg krowy.net.2026-01-13110345 krowy krowy.net --webp --zip

# Use custom directories
./build/ssg my-content my-template example.com \
  --content-dir=/data/content \
  --templates-dir=/data/templates \
  --output-dir=/var/www/html

# Or using Makefile
make generate        # krowy template
make generate-simple # simple template
make serve           # generate and run local server
make deploy          # generate with WebP + ZIP for Cloudflare Pages

Output

Generated files will be in the output/ folder:

output/
β”œβ”€β”€ index.html          # Homepage
β”œβ”€β”€ css/
β”‚   └── style.css       # Stylesheet
β”œβ”€β”€ js/
β”‚   └── main.js         # JavaScript
β”œβ”€β”€ media/              # Media files
β”œβ”€β”€ {slug}/             # Pages and posts (SEO URLs)
β”‚   └── index.html
β”œβ”€β”€ category/
β”‚   └── {category-slug}/
β”‚       └── index.html
β”œβ”€β”€ sitemap.xml         # Sitemap for search engines
β”œβ”€β”€ robots.txt          # Robots file
β”œβ”€β”€ _headers            # Cloudflare Pages headers
└── _redirects          # Cloudflare Pages redirects

πŸ”§ Template Engines

SSG supports multiple template engines. By default, Go templates are used, but you can switch to other engines:

Available Engines

Engine Flag Syntax Style
Go (default) --engine=go .Variable, range .Items
Pongo2 --engine=pongo2 Jinja2/Django: for item in items
Mustache --engine=mustache variable, #items
Handlebars --engine=handlebars variable, #each items

Usage Examples

# Use Pongo2 (Jinja2/Django syntax)
ssg my-content mytheme example.com --engine=pongo2

# Use Mustache
ssg my-content mytheme example.com --engine=mustache

# Use Handlebars
ssg my-content mytheme example.com --engine=handlebars

Online Themes

Download themes directly from GitHub, GitLab, or any ZIP URL:

# Download Hugo theme from GitHub
ssg my-content bearblog example.com --online-theme=https://github.com/janraasch/hugo-bearblog

# Download from any URL
ssg my-content mytheme example.com --online-theme=https://example.com/theme.zip

The theme will be downloaded and extracted to templates/{template-name}/.

Template Syntax Comparison

Go Templates:

{% raw %}

{{ range .Posts }}
  <h2>{{ .Title }}</h2>
  <p>{{ .Content }}</p>
{{ end }}

{% endraw %}

Pongo2 (Jinja2):

{% raw %}

{% for post in Posts %}
  <h2>{{ post.Title }}</h2>
  <p>{{ post.Content }}</p>
{% endfor %}

{% endraw %}

Mustache:

{% raw %}

{{#Posts}}
  <h2>{{Title}}</h2>
  <p>{{Content}}</p>
{{/Posts}}

{% endraw %}

Handlebars:

{% raw %}

{{#each Posts}}
  <h2>{{Title}}</h2>
  <p>{{Content}}</p>
{{/each}}

{% endraw %}

🎬 GitHub Actions

Use SSG as a GitHub Action in your CI/CD pipeline:

Versioning

Reference Description
spagu/ssg@main Latest from main branch (development)
spagu/ssg@v1 Latest stable v1.x release
spagu/ssg@v1.3.0 Specific version

Note: Use @main until a stable release is published.

Basic Usage

- name: Generate static site
  uses: spagu/ssg@main  # or @v1 after release
  with:
    source: 'my-content'
    template: 'krowy'
    domain: 'example.com'

Full Configuration

{% raw %}

- name: Generate static site
  id: ssg
  uses: spagu/ssg@v1
  with:
    source: 'my-content'           # Content folder (inside content/)
    template: 'krowy'              # Template: 'simple' or 'krowy'
    domain: 'example.com'          # Target domain
    version: 'latest'              # Optional: SSG version (default: latest)
    content-dir: 'content'         # Optional: content directory path
    templates-dir: 'templates'     # Optional: templates directory path
    output-dir: 'output'           # Optional: output directory path
    webp: 'true'                   # Optional: convert images to WebP
    webp-quality: '80'             # Optional: WebP quality 1-100 (default: 60)
    zip: 'true'                    # Optional: create ZIP for deployment
    minify: 'true'                 # Optional: minify HTML/CSS/JS
    clean: 'true'                  # Optional: clean output before build

- name: Show outputs
  run: |
    echo "Output path: ${{ steps.ssg.outputs.output-path }}"
    echo "ZIP file: ${{ steps.ssg.outputs.zip-file }}"
    echo "ZIP size: ${{ steps.ssg.outputs.zip-size }} bytes"

{% endraw %}

Action Inputs

Input Description Required Default
source Content source folder name βœ… -
template Template name βœ… simple
domain Target domain βœ… -
version SSG version to download ❌ latest
content-dir Path to content directory ❌ content
templates-dir Path to templates directory ❌ templates
output-dir Path to output directory ❌ output
webp Convert images to WebP ❌ false
webp-quality WebP compression quality 1-100 ❌ 60
zip Create ZIP file ❌ false
minify Minify HTML, CSS, and JS ❌ false
clean Clean output directory before build ❌ false

Action Outputs

Output Description
output-path Path to generated site directory
zip-file Path to ZIP file (if --zip used)
zip-size Size of ZIP file in bytes

Deploy to Cloudflare Pages

{% raw %}

name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Generate site
        id: ssg
        uses: spagu/ssg@v1
        with:
          source: 'my-content'
          template: 'krowy'
          domain: 'example.com'
          webp: 'true'

      - name: Deploy to Cloudflare
        uses: cloudflare/pages-action@v1
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          projectName: 'my-site'
          directory: ${{ steps.ssg.outputs.output-path }}

{% endraw %}

πŸ“ Complete workflow examples are available in examples/workflows/.

πŸ“ Project Structure

ssg/
β”œβ”€β”€ cmd/
β”‚   └── ssg/
β”‚       └── main.go           # CLI entry point
β”œβ”€β”€ internal/
β”‚   β”œβ”€β”€ generator/
β”‚   β”‚   β”œβ”€β”€ generator.go      # Generator logic
β”‚   β”‚   β”œβ”€β”€ generator_test.go # Generator tests
β”‚   β”‚   └── templates.go      # Default HTML templates
β”‚   β”œβ”€β”€ models/
β”‚   β”‚   └── content.go        # Data models
β”‚   └── parser/
β”‚       β”œβ”€β”€ markdown.go       # Markdown parser
β”‚       └── markdown_test.go  # Parser tests
β”œβ”€β”€ content/                  # Source data
β”‚   └── {source}/
β”‚       β”œβ”€β”€ metadata.json
β”‚       β”œβ”€β”€ media/
β”‚       β”œβ”€β”€ pages/
β”‚       └── posts/
β”œβ”€β”€ templates/                # Templates
β”‚   β”œβ”€β”€ simple/
β”‚   β”‚   β”œβ”€β”€ css/
β”‚   β”‚   └── js/
β”‚   └── krowy/
β”‚       β”œβ”€β”€ css/
β”‚       └── js/
β”œβ”€β”€ output/                   # Generated site (gitignored)
β”œβ”€β”€ go.mod
β”œβ”€β”€ go.sum
β”œβ”€β”€ Makefile
β”œβ”€β”€ README.md
β”œβ”€β”€ CHANGELOG.md
β”œβ”€β”€ .gitignore
└── .dockerignore

🎨 Templates

simple - Modern Dark Theme

Elegant dark theme with glassmorphism and gradients:

  • Dark background: #0f0f0f
  • Cards: #222222
  • Accent: purple gradient #6366f1 β†’ #a855f7
  • Hover animations and micro-interactions

krowy - Green Farm Theme

Natural light theme inspired by krowy.net:

  • Light background: #f8faf5
  • Cards: #ffffff
  • Accent: green #2d7d32
  • Cow icon πŸ„ in logo
  • Nature and ecology focus

🎨 Styles/Colors

Color Guidelines (WCAG 2.2 Compliant)

Simple Template (Dark)

/* Background */
--color-bg-primary: #0f0f0f;
--color-bg-secondary: #1a1a1a;
--color-bg-card: #222222;

/* Text (minimum contrast 4.5:1) */
--color-text-primary: #ffffff;
--color-text-secondary: #b3b3b3;
--color-text-muted: #808080;

/* Accent */
--color-accent: #6366f1;
--gradient-primary: linear-gradient(135deg, #6366f1 0%, #8b5cf6 50%, #a855f7 100%);

Krowy Template (Light)

/* Background */
--color-bg-primary: #f8faf5;
--color-bg-secondary: #ffffff;
--color-bg-card: #ffffff;

/* Text (minimum contrast 4.5:1) */
--color-text-primary: #1a2e1a;
--color-text-secondary: #3d5a3d;
--color-text-muted: #6b8a6b;

/* Accent */
--color-accent: #2d7d32;
--gradient-primary: linear-gradient(135deg, #2d7d32 0%, #43a047 50%, #66bb6a 100%);

Detailed style documentation: docs/STYLES.md

πŸ—οΈ Architecture

flowchart TB
    subgraph Input["πŸ“₯ Input"]
        A[content/source] --> B[metadata.json]
        A --> C[pages/*.md]
        A --> D[posts/**/*.md]
        A --> E[media/*]
    end

    subgraph Processing["βš™οΈ Processing"]
        F[Parser] --> G[Models]
        G --> H[Generator]
        T[Templates] --> H
    end

    subgraph Output["πŸ“€ Output"]
        H --> I[output/]
        I --> J[index.html]
        I --> K[pages/]
        I --> L[posts/]
        I --> M[category/]
        I --> N[css/]
        I --> O[js/]
        I --> P[media/]
    end

    B --> F
    C --> F
    D --> F
    E --> P
Loading

πŸ§ͺ Testing

# Run all tests
make test

# Tests with coverage
make test-coverage

# Open coverage report
open coverage.html

πŸ› οΈ Development

Available Make Commands

make help           # Show all commands
make all            # deps + lint + test + build
make build          # Build binary
make test           # Run tests
make lint           # Check code
make run            # Build and run
make generate       # Generate site (krowy template)
make generate-simple # Generate site (simple template)
make serve          # Generate and serve locally
make deploy         # Generate with WebP + ZIP for Cloudflare Pages
make clean          # Clean artifacts
make install        # Install binary to /usr/local/bin

Creating Your Own Template

  1. Create a folder in templates/your-template-name/
  2. Add files:
    • css/style.css
    • js/main.js (optional)
    • index.html, page.html, post.html, category.html (optional)
  3. HTML templates are generated automatically if missing

πŸ“„ License

BSD 3-Clause License - see LICENSE

πŸ‘₯ Authors

About

A simple static site generator written in Go. Converts content from WordPress exports (Markdown format with YAML frontmatter) to static HTML, CSS, and JS files.

Resources

License

BSD-3-Clause, BSD-3-Clause licenses found

Licenses found

BSD-3-Clause
LICENSE
BSD-3-Clause
LICENSE.md

Code of conduct

Security policy

Stars

Watchers

Forks

Sponsor this project

Packages