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.
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.
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.
validate_preview_urls.py¶
Validate preview image URLs in frontmatter.
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:
--modelinput >AI_MODEL>_data/ai.ymlmodel:> the fleet default. - Auth from the env, OAuth first:
CLAUDE_CODE_OAUTH_TOKEN(preferred; stripsANTHROPIC_API_KEYfrom the CLI's env) orANTHROPIC_API_KEY(also the only credential the API fallback can use). - Exit codes:
0when the call ran or nothing was ever attempted (no auth — the documented no-op);1when 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.
analyze-commits¶
Analyze commits to determine version bump type.
fix-markdown¶
Fix markdown formatting issues.
Libraries (scripts/lib/)¶
These are sourced by other scripts, not executed directly:
common.sh- Logging utilities, colors, dry-run supportfrontmatter.sh- Config-aware frontmatter extraction, validation, and fix helpersversion.sh- Version parsing, calculation, file updatesvalidation.sh- Environment and dependency validationgit.sh- Git operations (tags, commits, branches)changelog.sh- Changelog generation from commitsgem.sh- Gem build/publish operationspreview_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¶
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:
- RubyGems account: Sign up at rubygems.org
- API key: Get from your RubyGems account settings
- GitHub secret: Add
RUBYGEMS_API_KEYto repository secrets
Local Authentication¶
CI/CD Integration¶
The automation system is designed for complete CI/CD integration:
Local Development¶
- Setup:
./scripts/setup.sh - Development: Make changes
- Testing:
./scripts/test.sh - Version:
./scripts/version.sh - Build:
./scripts/build.sh
Automated Release¶
- Manual trigger: Version bump workflow
- Automatic testing: CI workflow validates changes
- Tag creation: Creates version tag
- Release build: Gem release workflow triggers
- Publication: Automatic publishing to RubyGems
Error Handling¶
Common Issues¶
"Working directory is not clean"¶
"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"¶
"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-runto 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.shto 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:
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:
- Test thoroughly: Run all scripts with
--dry-runfirst - Update documentation: Reflect changes in this README
- Follow conventions: Match existing script patterns
- Error handling: Include proper error messages and exit codes
- 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:
- Content Analysis: Extracts title, description, categories, and content to generate meaningful prompts
- Smart Prompts: Creates detailed image generation prompts based on article content
- Front Matter Updates: Automatically updates the markdown file with the new preview path
- 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:
- Check logs: Review GitHub Actions logs
- Run locally: Test scripts on your local machine
- Validate environment: Ensure all dependencies are installed
- Create issue: Report bugs with full error output and system info