Skip to content

Gem Automation Scripts

This directory contains automation scripts for managing the jekyll-theme-zer0 gem lifecycle and feature modules.

Directory Structure

scripts/
├── bin/                    # Entry point commands (use these!)
│   ├── build              # Build gem without releasing
│   ├── validate           # Preflight checks for local/CI validation
│   ├── release            # Full release workflow
│   └── test               # Run all test suites
├── lib/                   # Shared libraries (sourced, not executed)
│   ├── common.sh          # Logging, utilities, dry-run support
│   ├── version.sh         # Version management functions
│   ├── validation.sh      # Environment validation
│   ├── git.sh             # Git operations
│   ├── changelog.sh       # Changelog generation
│   ├── gem.sh             # Gem build/publish
│   └── preview_generator.py  # Preview-image engine (Claude orchestrates; ZER0-004)
├── features/              # Feature-specific scripts
│   ├── generate-preview-images     # AI preview image generator
│   ├── pixelate-preview-images     # Shrink preview banners (pixelate + PNG-8)
│   ├── pixelate_images.py          # Pure-stdlib pixelate/quantize engine
│   ├── install-preview-generator   # Preview generator installer
│   └── validate_preview_urls.py    # Preview URL validator
├── ai/                    # Consumer companions to the hub's `ai-runner` kit (the runner itself is referenced, not stored)
│   ├── api_call.rb                 # Single-shot Claude API fallback (stdlib only)
│   ├── usage.rb                    # Optional metering: one JSONL record per call
│   ├── usage_report.rb             # Publishes metering (summary, artifact, PR comment)
│   └── README.md                   # What stays here and why
├── ci/                    # Helpers called directly by workflow steps
│   ├── classify_changes.py         # Change classifier (issue-pr-auto-merge)
│   ├── agent_review_result.py      # Did the Claude content review actually run?
│   └── test_agent_review_result.py # …its tests (CI-run via test/lib bridge)
├── utils/                 # Utility scripts
│   ├── analyze-commits    # Commit analyzer for version bumps
│   ├── fix-markdown       # Markdown formatting fixer
│   └── setup              # Development environment setup
├── test/                  # Test suites
│   ├── lib/               # Library unit tests
│   ├── theme/             # Theme validation tests
│   └── integration/       # Integration tests
└── *.sh (wrappers)        # Backward-compatible wrappers

Quick Start

# Build gem
./scripts/bin/build

# Preflight validation
./scripts/bin/validate --quick
./scripts/bin/validate --start-docker

# Full release workflow
./scripts/bin/release patch   # or minor/major

# Run tests
./scripts/bin/test

Scripts Overview

Main Commands (scripts/bin/)

bin/build

Build the gem without the full release workflow.

./scripts/bin/build [--dry-run] [--verbose]

bin/validate

Run preflight validation before refactors, pull requests, and releases. The quick path validates repository files, version consistency, YAML parsing, active configuration contracts, config-file classification, and navigation data before the Docker/local build stages run.

./scripts/bin/validate [options]

Options:
  --quick             Host-only checks for CI fast feedback
  --full              Include tests, Obsidian tests, and HTMLProofer
  --start-docker      Start the jekyll Docker Compose service if needed
  --docker            Require Docker Compose for Jekyll commands
  --local             Require local bundle exec for Jekyll commands

bin/release

Full release workflow with changelog, version bump, and publishing.

./scripts/bin/release [patch|minor|major] [options]

Options:
  --dry-run           Preview without making changes
  --skip-tests        Skip test execution
  --skip-publish      Skip RubyGems publishing
  --no-github-release Skip GitHub release creation
  --non-interactive   No confirmation prompts

bin/test

Unified test runner for all test suites.

./scripts/bin/test [all|lib|theme|integration] [--verbose]

Feature Scripts (scripts/features/)

generate-preview-images

AI-powered preview image generator for Jekyll posts.

./scripts/features/generate-preview-images [options]

Options:
  --list-missing      List files missing previews
  --dry-run           Preview without changes
  --collection TYPE   Generate for specific collection (posts, docs, etc.)
  -f, --file PATH     Process specific file
  --provider PROVIDER Renderer (openai, xai, stability, gemini, local)
  --prompt-engine ENG claude analyzes the article (default) | template
  --review ENG        claude reviews the render (default) | none
  --assets-prefix     Custom assets path prefix (default: /assets)
  --no-auto-prefix    Disable automatic path prefixing

Renderers (Claude orchestrates all of them — analysis + review via
CLAUDE_CODE_OAUTH_TOKEN / ANTHROPIC_API_KEY / logged-in `claude` CLI):
  openai    - OpenAI gpt-image-2 / DALL-E, default (requires OPENAI_API_KEY)
  xai       - xAI Grok image generation (requires XAI_API_KEY)
  stability - Stability AI (requires STABILITY_API_KEY)
  gemini    - Google Gemini (requires GEMINI_API_KEY)
  local     - Deterministic template SVG/PNG (no API key)

pixelate-preview-images

Pixelate + palette-quantize the preview banners so they are dramatically smaller files while retaining the retro pixel-art look. Pure Python stdlib (no ImageMagick / Pillow / pngquant needed) — it downsamples the image and reduces it to an indexed PNG-8 palette. Typical savings on the AI-generated banners are ~90% (e.g. a 2.7 MB banner becomes ~230 KB).

# Preview the savings for every banner (no changes), 4 workers:
./scripts/features/pixelate-preview-images --dry-run -j 4

# Optimize all banners in place:
./scripts/features/pixelate-preview-images -j 4

# Chunkier pixel-art look for one image:
./scripts/features/pixelate-preview-images --block 6 --colors 64 \
    assets/images/previews/about.png

Options (forwarded to scripts/features/pixelate_images.py):
  -n, --dry-run         Report savings without writing
  --colors N            Palette size 2-256 (default: 256)
  --bits N              Colour precision 1-8 while quantizing (default: 6)
  --max-width N         Downscale so width <= N, keep aspect (default: 1024)
  --scale F             Scale factor, e.g. 0.5
  --block N             Average NxN source pixels per output pixel (chunky)
  --filter nearest|box  Downsample filter (default: nearest)
  --upscale             Keep original WxH (chunky pixels) instead of reduced
  --backup              Keep <file>.orig when writing in place
  --force               Write even if the result is not smaller
  -j, --jobs N          Parallel worker processes (default: 1)

With no path argument it processes preview_images.output_dir from _config.yml (default assets/images/previews). Non-PNG and 16-bit/interlaced inputs are skipped gracefully. The engine has a built-in --selftest, exercised by scripts/test/lib/test_pixelate_images.sh.

install-preview-generator

Install the preview image generator feature.

./scripts/features/install-preview-generator [options]

validate_preview_urls.py

Validate preview image URLs in frontmatter.

python3 scripts/features/validate_preview_urls.py [--verbose] [--suggestions]

AI Runner (scripts/ai/)

Every model call a workflow makes goes through uses: bamr87/bamr87/.github/actions/claude-run@main — the fleet's shared ai-runner kit, versioned once in the bamr87/bamr87 hub and consumed by reference. The runner (run.sh) and the composite action are no longer stored in this repo; the hub's kit README (templates/ai-runner/README.md) is the contract. In short:

  • Model: --model input > AI_MODEL > _data/ai.yml model: > the fleet default.
  • Auth from the env, OAuth first: CLAUDE_CODE_OAUTH_TOKEN (preferred; strips ANTHROPIC_API_KEY from the CLI's env) or ANTHROPIC_API_KEY (also the only credential the API fallback can use).
  • Exit codes: 0 when the call ran or nothing was ever attempted (no auth — the documented no-op); 1 when the call was attempted and failed with no usable fallback, with the reason raised as a ::error:: annotation.

This directory holds the consumer-owned companions the hub runner probes for in the checkout and uses when present: usage.rb metering (prices from _data/ai_pricing.yml), usage_report.rb publishing, api_call.rb fallback — plus tools/unwrap-prose.py as the post-run prose normalizer. See scripts/ai/README.md. The exit-code contract test lives in the hub (templates/ai-runner/tests/contract.sh) and runs there on every runner change.

CI Helpers (scripts/ci/)

agent_review_result.py

Decides whether the Claude tier of ai-content-review.yml actually produced a review, and fails the job when it did not.

Before this existed, the agent step captured the Claude CLI's exit status, echoed it, and then posted whatever landed on stdout as the review. When the OAuth credential was revoked, Failed to authenticate. API Error: 401 OAuth access token has been revoked. became the "review" and every job still reported success (issue #418) — for days, across several PRs, with nothing on the checks list to say the editorial review had stopped happening.

Note that a non-zero exit code would not have caught it: the CLI reported the auth failure on stdout. Three conditions are classified — a non-zero exit, no output at all, and a short output that leads with a known CLI failure signature. Any of them writes an explicit failure notice for the sticky comment, emits a ::error:: annotation, and exits 1.

python3 scripts/ci/agent_review_result.py \
  --status "$status" --stdout /tmp/agent-review.md \
  --stderr /tmp/agent-err.log --out /tmp/agent-review-final.md
# exit 0 = the review is real · 1 = the tier failed · 2 = bad invocation

python3 scripts/ci/test_agent_review_result.py   # tests, incl. the verbatim #418 output

The tests run in CI through scripts/test/lib/test_agent_review_result.sh, which run_tests.sh sources and ./scripts/bin/test executes on every PR.

classify_changes.py

Classifies a PR's changed files for issue-pr-auto-merge.yml (--content-only).

Content Validation

lint-pages

Config-driven frontmatter validator for all Jekyll collections.

./scripts/lint-pages [options]

Options:
  --strict              Exit non-zero on any violation
  --warn                Print warnings only (default)
  --fix                 Auto-fix safe violations (dates, drafts, field renames)
  --dry-run             Preview fixes without modifying files
  --report              Output structured report summary
  --verbose, -v         Detailed output
  --collection NAME     Validate only the named collection
  --schema PATH         Path to schema YAML (default: .github/config/frontmatter_schema.yml)
  --rules PATH          Path to content rules YAML (default: .github/config/content_rules.yml)

Environment Variables:
  FRONTMATTER_SCHEMA_PATH   Alternate schema path
  CONTENT_RULES_PATH        Alternate content rules path
  FRONTMATTER_STRICT        Set to "true" for strict mode

Configuration files: - .github/config/frontmatter_schema.yml — Defines required/optional fields, layout constraints, field types, and canonical field mappings per collection. - .github/config/content_rules.yml — Behavioral rules such as strictness per environment, auto-fixable violation types, and template-to-collection mappings.

Examples:

# Validate all pages (warn mode)
./scripts/lint-pages

# CI: fail on violations
./scripts/lint-pages --strict

# Preview auto-fixes without modifying files
./scripts/lint-pages --fix --dry-run

# Debug a specific collection
./scripts/lint-pages --collection posts --verbose

Utility Scripts (scripts/utils/)

setup

Set up the development environment.

./scripts/utils/setup

analyze-commits

Analyze commits to determine version bump type.

./scripts/utils/analyze-commits [range]

fix-markdown

Fix markdown formatting issues.

./scripts/utils/fix-markdown [files...]

Libraries (scripts/lib/)

These are sourced by other scripts, not executed directly:

  • common.sh - Logging utilities, colors, dry-run support
  • frontmatter.sh - Config-aware frontmatter extraction, validation, and fix helpers
  • version.sh - Version parsing, calculation, file updates
  • validation.sh - Environment and dependency validation
  • git.sh - Git operations (tags, commits, branches)
  • changelog.sh - Changelog generation from commits
  • gem.sh - Gem build/publish operations
  • preview_generator.py - Preview-image engine (Claude analyze/review + openai/xai/stability/gemini/local renderers)

Test Suites (scripts/test/)

# Run all library unit tests
./scripts/test/lib/run_tests.sh

# Run theme validation
./scripts/test/theme/validate

# Run integration tests
./scripts/test/integration/auto-version
./scripts/test/integration/mermaid

Backward Compatibility

Legacy script paths (e.g., ./scripts/build.sh) are maintained as thin wrappers that forward to the canonical locations in bin/, utils/, features/, or test/.

Development Workflow

Initial Setup

git clone https://github.com/bamr87/zer0-mistakes.git
cd zer0-mistakes
./scripts/utils/setup

Making Changes

# Make your changes

# Run tests
./scripts/bin/test

# Build (without publish)
./scripts/bin/build

# Full release
./scripts/bin/release patch

Automated Workflows

The project includes GitHub Actions workflows for automation:

CI Workflow (.github/workflows/ci.yml)

  • Triggers on: Push to main/develop, Pull Requests
  • Tests on multiple Ruby versions (2.7, 3.0, 3.1, 3.2)
  • Runs linting and security scans
  • Validates gem building

Release Workflow (.github/workflows/gem-release.yml)

  • Triggers on: Git tags (v*), Manual dispatch
  • Builds and tests the gem
  • Publishes to RubyGems (production environment)
  • Creates GitHub release with gem attached

Version Bump Workflow (.github/workflows/version-bump.yml)

  • Manual trigger with version type selection
  • Runs tests before bumping
  • Creates version bump commit and tag
  • Optionally creates PR for review

Requirements

System Dependencies

  • Bash: >= 3.2 (macOS default supported - no Homebrew Bash required!)
  • Ruby: >= 2.7.0
  • Bundler: For dependency management
  • jq: For JSON processing
  • Git: For version control

Note on Bash Compatibility: All scripts are compatible with Bash 3.2+ (the default version on macOS). You do NOT need to install Homebrew Bash. The release automation, changelog generation, and all CI/CD workflows work seamlessly with the system-provided Bash on macOS and standard Bash installations on Linux.

RubyGems Publishing Setup

To publish gems, you need:

  1. RubyGems account: Sign up at rubygems.org
  2. API key: Get from your RubyGems account settings
  3. GitHub secret: Add RUBYGEMS_API_KEY to repository secrets

Local Authentication

# Sign in to RubyGems locally
gem signin

# Verify authentication
gem whoami

CI/CD Integration

The automation system is designed for complete CI/CD integration:

Local Development

  1. Setup: ./scripts/setup.sh
  2. Development: Make changes
  3. Testing: ./scripts/test.sh
  4. Version: ./scripts/version.sh
  5. Build: ./scripts/build.sh

Automated Release

  1. Manual trigger: Version bump workflow
  2. Automatic testing: CI workflow validates changes
  3. Tag creation: Creates version tag
  4. Release build: Gem release workflow triggers
  5. Publication: Automatic publishing to RubyGems

Error Handling

Common Issues

"Working directory is not clean"

# Check status
git status

# Commit or stash changes
git add .
git commit -m "Your changes"

"Not authenticated with RubyGems"

# Sign in to RubyGems
gem signin

# Or set up API key
echo ":rubygems_api_key: YOUR_API_KEY" > ~/.gem/credentials
chmod 600 ~/.gem/credentials

"jq command not found"

# macOS
brew install jq

# Ubuntu/Debian
sudo apt-get install jq

"Gemspec validation failed"

# Check gemspec syntax
gem specification jekyll-theme-zer0.gemspec

# Common fixes:
# - Ensure all required files exist
# - Check Ruby version compatibility
# - Validate dependency versions

Best Practices

Version Management

  • Patch: Bug fixes, small improvements
  • Minor: New features, backward compatible
  • Major: Breaking changes

Testing

  • Always run tests before version bumps
  • Use --dry-run to preview changes
  • Test builds before publishing

Git Workflow

  • Keep working directory clean
  • Use descriptive commit messages
  • Tag releases consistently

Security

  • Never commit API keys
  • Use GitHub secrets for CI/CD
  • Regularly update dependencies

Monitoring and Maintenance

Regular Tasks

  • Weekly: Run ./scripts/test.sh to catch issues early
  • Monthly: Review and update dependencies
  • Per release: Monitor RubyGems downloads and feedback

Monitoring Points

  • CI/CD status: GitHub Actions dashboard
  • Gem stats: RubyGems.org gem page
  • Security: Dependabot alerts
  • Performance: Build times and test results

Troubleshooting

Debug Mode

Most scripts support verbose output:

./scripts/test.sh --verbose
./scripts/build.sh --dry-run

Manual Recovery

If automation fails, you can manually:

# Reset version
git checkout package.json
git reset HEAD~1

# Rebuild gem
rm -f *.gem
gem build jekyll-theme-zer0.gemspec

# Force push (use with caution)
git push --force-with-lease

Contributing

When contributing to the automation system:

  1. Test thoroughly: Run all scripts with --dry-run first
  2. Update documentation: Reflect changes in this README
  3. Follow conventions: Match existing script patterns
  4. Error handling: Include proper error messages and exit codes
  5. Backwards compatibility: Ensure existing workflows continue working

🎨 generate-preview-images.sh

AI-powered preview image generator for Jekyll posts, articles, and quests. Automatically scans content files, detects missing preview images, and generates them using AI providers (OpenAI DALL-E, Stability AI).

Usage

# List all files missing preview images
./scripts/generate-preview-images.sh --list-missing

# Dry run to see what would be generated
./scripts/generate-preview-images.sh --dry-run --verbose

# Generate images for posts collection
./scripts/generate-preview-images.sh --collection posts

# Generate image for a specific file
./scripts/generate-preview-images.sh -f pages/_posts/my-article.md

# Force regenerate all images
./scripts/generate-preview-images.sh --force

# Use different AI provider
./scripts/generate-preview-images.sh --provider stability

Options

Option Description
-h, --help Show help message
-d, --dry-run Preview without making changes
-v, --verbose Enable verbose output
-f, --file FILE Process a specific file only
-c, --collection NAME Process collection (posts, quickstart, docs, all)
-p, --provider PROVIDER AI provider: openai, stability, local
--output-dir DIR Output directory (default: assets/images/previews)
--force Regenerate even if preview exists
--list-missing Only list files with missing previews

Environment Variables

Variable Required Description
OPENAI_API_KEY For OpenAI OpenAI API key for DALL-E
STABILITY_API_KEY For Stability Stability AI API key
IMAGE_STYLE No Custom style prompt
IMAGE_SIZE No Image dimensions (default: 1024x1024)
IMAGE_MODEL No OpenAI model (default: dall-e-3)

AI Agent Integration

The script is designed to integrate with AI agents for automated content management:

  1. Content Analysis: Extracts title, description, categories, and content to generate meaningful prompts
  2. Smart Prompts: Creates detailed image generation prompts based on article content
  3. Front Matter Updates: Automatically updates the markdown file with the new preview path
  4. Idempotent: Won't regenerate images that already exist (unless --force)

Engine

All logic lives in the single-file Python engine scripts/lib/preview_generator.py (the shell entry points are thin wrappers around it):

# Install the one dependency
pip3 install pyyaml

# Run the engine directly (same flags as the wrapper)
python3 scripts/lib/preview_generator.py --collection posts --dry-run

Example Workflow

# 1. Check which files need preview images
./scripts/generate-preview-images.sh --list-missing

# 2. Preview what would be generated
export OPENAI_API_KEY="your-api-key"
./scripts/generate-preview-images.sh --dry-run

# 3. Generate images for specific collection
./scripts/generate-preview-images.sh --collection posts

# 4. Verify results and commit
git status
git add assets/images/previews/ pages/
git commit -m "feat: add AI-generated preview images"

Support

For issues with the automation system:

  1. Check logs: Review GitHub Actions logs
  2. Run locally: Test scripts on your local machine
  3. Validate environment: Ensure all dependencies are installed
  4. Create issue: Report bugs with full error output and system info