From 1adbd1a5ffd28f1c7d49aad8b21850d9e3785912 Mon Sep 17 00:00:00 2001 From: Thomas Hallock Date: Tue, 9 Sep 2025 15:55:53 -0500 Subject: [PATCH] feat: add CI-friendly example generation and verification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add 'make examples' to regenerate README images - Add 'make verify-examples' for CI verification - Create GitHub Action to verify examples are up to date - Improve generate_examples.py with better error handling - Add update-examples.sh convenience script - Document development workflow in README This ensures README examples always match the actual code output. šŸ¤– Generated with Claude Code Co-Authored-By: Claude --- .github/workflows/verify-examples.yml | 52 +++++++++++++++++++++++++++ Makefile | 21 ++++++++++- README.md | 32 +++++++++++++++++ scripts/update-examples.sh | 25 +++++++++++++ src/generate_examples.py | 32 ++++++++++++++--- 5 files changed, 156 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/verify-examples.yml create mode 100755 scripts/update-examples.sh diff --git a/.github/workflows/verify-examples.yml b/.github/workflows/verify-examples.yml new file mode 100644 index 00000000..06b51b16 --- /dev/null +++ b/.github/workflows/verify-examples.yml @@ -0,0 +1,52 @@ +name: Verify Examples + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + paths: + - 'src/**' + - 'templates/**' + - 'docs/images/**' + - '.github/workflows/verify-examples.yml' + +jobs: + verify-examples: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y poppler-utils qpdf + + - name: Install Typst + run: | + # Download and install Typst + wget https://github.com/typst/typst/releases/latest/download/typst-x86_64-unknown-linux-musl.tar.xz + tar -xf typst-x86_64-unknown-linux-musl.tar.xz + sudo mv typst-x86_64-unknown-linux-musl/typst /usr/local/bin/ + typst --version + + - name: Install Python dependencies + run: | + pip install pyyaml + + - name: Verify examples are up to date + run: | + make verify-examples + + - name: Upload example images if changed + if: failure() + uses: actions/upload-artifact@v3 + with: + name: updated-examples + path: docs/images/ \ No newline at end of file diff --git a/Makefile b/Makefile index 46dad7ae..f4df5ebf 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all clean install test samples help check-deps +.PHONY: all clean install test samples help check-deps examples verify-examples # Default target all: out/flashcards.pdf out/flashcards_linear.pdf @@ -42,6 +42,23 @@ test: check-deps @command -v qpdf >/dev/null 2>&1 && qpdf --check out/test.pdf || echo "PDF generated (validation skipped)" @echo "Test completed successfully" +# Generate README example images +examples: check-deps + @echo "Generating example images for README..." + @python3 src/generate_examples.py + @echo "āœ“ Example images generated in docs/images/" + +# Verify examples are up to date (for CI) +verify-examples: examples + @echo "Verifying example images are up to date..." + @if git diff --quiet docs/images/; then \ + echo "āœ“ Example images are up to date"; \ + else \ + echo "āœ— Example images have changed - please run 'make examples' and commit the changes"; \ + git diff --stat docs/images/; \ + exit 1; \ + fi + # Clean output files clean: rm -rf out/ @@ -53,6 +70,8 @@ help: @echo " make Generate default flashcards (0-9)" @echo " make samples Generate all sample configurations" @echo " make test Run a quick test build" + @echo " make examples Generate README example images" + @echo " make verify-examples Verify examples are up to date (CI)" @echo " make install Install dependencies (macOS)" @echo " make clean Remove all generated files" @echo " make help Show this help message" diff --git a/README.md b/README.md index 71765a9f..708bcf2a 100644 --- a/README.md +++ b/README.md @@ -394,6 +394,38 @@ The `SorobanGenerator` class provides: All methods use clean TypeScript interfaces with proper types - no shell command building required! +## Development + +### Updating Example Images + +If you make changes that affect the visual output, please update the example images: + +```bash +# Regenerate all example images +make examples + +# Or use the update script +./scripts/update-examples.sh + +# Verify examples are up to date (CI will also check this) +make verify-examples +``` + +The CI pipeline will automatically verify that example images are up to date with the code. + +### Running Tests + +```bash +# Quick test build +make test + +# Generate all samples +make samples + +# Full CI verification +make verify-examples +``` + ## License MIT License - see LICENSE file for details. diff --git a/scripts/update-examples.sh b/scripts/update-examples.sh new file mode 100755 index 00000000..7feeaae6 --- /dev/null +++ b/scripts/update-examples.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# Update README example images +# Run this script when you make changes that affect the visual output + +set -e + +echo "šŸ”„ Updating README example images..." + +# Run the example generator +if ! python3 src/generate_examples.py; then + echo "āŒ Failed to generate examples" + exit 1 +fi + +# Check if there are changes +if git diff --quiet docs/images/; then + echo "āœ… No changes to example images" +else + echo "šŸ“ Example images have been updated:" + git diff --stat docs/images/ + echo "" + echo "Please review the changes and commit them if they look correct:" + echo " git add docs/images/" + echo " git commit -m 'docs: update example images'" +fi \ No newline at end of file diff --git a/src/generate_examples.py b/src/generate_examples.py index 5620dc0f..2f586948 100755 --- a/src/generate_examples.py +++ b/src/generate_examples.py @@ -183,8 +183,25 @@ def main(): examples = generate_example_pdfs() + # Check if we have a PDF to PNG converter available + converters = [] + for cmd in ['pdftoppm', 'convert', 'gs']: + if subprocess.run(['which', cmd], capture_output=True).returncode == 0: + converters.append(cmd) + + if not converters: + print("ERROR: No PDF to PNG converter found. Please install one of:") + print(" - poppler-utils (for pdftoppm)") + print(" - imagemagick (for convert)") + print(" - ghostscript (for gs)") + print("\nOn Ubuntu/Debian: sudo apt-get install poppler-utils") + print("On macOS: brew install poppler") + return 1 + + print(f"Using PDF converters: {', '.join(converters)}") print("Generating example images...") + failed = [] for example in examples: print(f" - {example['name']}: {example['desc']}") @@ -207,7 +224,8 @@ def main(): if pdf_to_png(str(pdf_path), str(front_png), dpi=150, page=1): print(f" āœ“ Generated {front_png.name}") else: - print(f" WARNING: Could not convert to PNG (install pdftoppm, ImageMagick, or Ghostscript)") + print(f" āœ— Failed to convert {front_png.name}") + failed.append(example['name']) # For single cards, also generate back page if '--cards-per-page' in example['args'] and example['args'][example['args'].index('--cards-per-page') + 1] == '1': @@ -215,9 +233,13 @@ def main(): if pdf_to_png(str(pdf_path), str(back_png), dpi=150, page=2): print(f" āœ“ Generated {back_png.name}") - print("\nExample images generated in docs/images/") - print("\nTo use in README:") - print("![Description](docs/images/example-name-front.png)") + if failed: + print(f"\nāœ— Failed to generate {len(failed)} images: {', '.join(failed)}") + return 1 + else: + print(f"\nāœ“ Successfully generated all {len(examples)} examples in docs/images/") + return 0 if __name__ == '__main__': - main() \ No newline at end of file + import sys + sys.exit(main()) \ No newline at end of file