add automation to create image-manifest.json

Hunter
Friday, July 11th, 2025 at 6:08 PM

.github/workflows/generate-manifest.ymlnew
generate_image_manifest.pynew
image-manifest.jsonnew
sw.js+14-22

.github/workflows/generate-manifest.yml
 1 +name: Generate Image Manifest
 2 +
 3 +on:
 4 +  push:
 5 +    branches: [ main ]
 6 +    paths: [ 'images/**' ]  # Only run when images are added/changed
 7 +  workflow_dispatch:  # Allow manual trigger
 8 +
 9 +jobs:
10 +  generate-manifest:
11 +    runs-on: ubuntu-latest
12 +    
13 +    steps:
14 +    - name: Checkout repository
15 +      uses: actions/checkout@v4
16 +      with:
17 +        token: ${{ secrets.GITHUB_TOKEN }}
18 +    
19 +    - name: Set up Python
20 +      uses: actions/setup-python@v4
21 +      with:
22 +        python-version: '3.x'
23 +    
24 +    - name: Generate image manifest
25 +      run: python generate_image_manifest.py
26 +    
27 +    - name: Check for changes
28 +      id: verify-changed-files
29 +      run: |
30 +        if [ -n "$(git status --porcelain)" ]; then
31 +          echo "changed=true" >> $GITHUB_OUTPUT
32 +        else
33 +          echo "changed=false" >> $GITHUB_OUTPUT
34 +        fi
35 +    
36 +    - name: Commit and push changes
37 +      if: steps.verify-changed-files.outputs.changed == 'true'
38 +      run: |
39 +        git config --local user.email "action@github.com"
40 +        git config --local user.name "GitHub Action"
41 +        git add image-manifest.json
42 +        git commit -m "Auto-update image manifest [skip ci]"
43 +        git push
generate_image_manifest.py
 1 +#!/usr/bin/env python3
 2 +"""
 3 +Generate image manifest for PWA caching
 4 +Scans the images/ directory and creates a JSON manifest file
 5 +"""
 6 +
 7 +import os
 8 +import json
 9 +from pathlib import Path
10 +
11 +def generate_image_manifest():
12 +    """Generate a JSON manifest of all images in the images/ directory"""
13 +    
14 +    # Define supported image extensions
15 +    image_extensions = {'.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico', '.bmp'}
16 +    
17 +    # Get the directory where this script is located
18 +    script_dir = Path(__file__).parent
19 +    images_dir = script_dir / 'images'
20 +    
21 +    # Check if images directory exists
22 +    if not images_dir.exists():
23 +        print(f"Images directory not found: {images_dir}")
24 +        return
25 +    
26 +    # Collect all image files
27 +    image_files = []
28 +    
29 +    for file_path in images_dir.iterdir():
30 +        if file_path.is_file() and file_path.suffix.lower() in image_extensions:
31 +            # Create relative path from web root
32 +            relative_path = f"/images/{file_path.name}"
33 +            image_files.append(relative_path)
34 +    
35 +    # Sort for consistent output
36 +    image_files.sort()
37 +    
38 +    # Create manifest object
39 +    manifest = {
40 +        "images": image_files,
41 +        "generated_at": "auto-generated by GitHub Actions",
42 +        "total_images": len(image_files)
43 +    }
44 +    
45 +    # Write manifest file
46 +    manifest_path = script_dir / 'image-manifest.json'
47 +    with open(manifest_path, 'w', encoding='utf-8') as f:
48 +        json.dump(manifest, f, indent=2, sort_keys=True)
49 +    
50 +    print(f"Generated manifest with {len(image_files)} images:")
51 +    for img in image_files:
52 +        print(f"  - {img}")
53 +    
54 +    return manifest
55 +
56 +if __name__ == "__main__":
57 +    manifest = generate_image_manifest()
58 +    print(f"\nManifest saved to: image-manifest.json")
image-manifest.json
 1 +{
 2 +  "generated_at": "auto-generated by GitHub Actions",
 3 +  "images": [
 4 +    "/images/ceedee.gif",
 5 +    "/images/floppy.gif",
 6 +    "/images/mac.png",
 7 +    "/images/pika_construction.gif",
 8 +    "/images/pizza.gif",
 9 +    "/images/rollerskate.png",
10 +    "/images/scared_mouse.gif",
11 +    "/images/sun.png",
12 +    "/images/underconstruction.gif",
13 +    "/images/welcome.gif",
14 +    "/images/windows.gif"
15 +  ],
16 +  "total_images": 11
17 +}
sw.js
   -const CACHE_NAME = 'web-workshop-v3';
 1 +const CACHE_NAME = 'web-workshop-v4';
 2  const urlsToCache = [
 3    '/',
 4    '/index.html',

44    }
45  }
46  
   -// Function to programmatically cache all images
47 +// Function to cache all images using the generated manifest
48  async function cacheImages(cache) {
   -  const imageExtensions = ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp'];
   -  
49    try {
   -    const response = await fetch('/images/');
50 +    const response = await fetch('/image-manifest.json');
51      if (response.ok) {
   -      const html = await response.text();
   -      const imagePattern = new RegExp(`href="([^"]*\\.(${imageExtensions.join('|')}))"`, 'gi');
   -      const links = html.match(imagePattern);
52 +      const manifest = await response.json();
53 +      console.log(`Caching ${manifest.total_images} images from manifest`);
54        
   -      if (links) {
   -        const imageFiles = links.map(link => {
   -          const match = link.match(/href="([^"]*)"/);
   -          return match ? '/images/' + match[1] : null;
   -        }).filter(Boolean);
   -        
   -        for (const imageFile of imageFiles) {
   -          try {
   -            await cache.add(imageFile);
   -            console.log(`Cached image: ${imageFile}`);
   -          } catch (e) {
   -            console.log(`Failed to cache image ${imageFile}:`, e);
   -          }
55 +      for (const imagePath of manifest.images) {
56 +        try {
57 +          await cache.add(imagePath);
58 +          console.log(`Cached image: ${imagePath}`);
59 +        } catch (e) {
60 +          console.log(`Failed to cache image ${imagePath}:`, e);
61          }
62        }
63 +    } else {
64 +      console.log('No image manifest found, skipping image caching');
65      }
66    } catch (e) {
   -    console.log('Failed to cache images directory:', e);
67 +    console.log('Failed to load image manifest:', e);
68    }
69  }
70