The website media workflow is repository-driven. The source files and the generated derivatives both belong in the repo.
Goals
- Source images are committed in the repository.
- Pre-generated WebP derivatives are committed in the repository.
- The committed social preview PNG is generated locally and committed in the repository.
- Source images and generated derivatives are tracked with Git LFS.
- Every raster source image has generated WebP derivatives.
- Local development and CI pull media from Git LFS before building.
- CI publishes built media from
website/publicrather than generating missing derivatives during deployment.
Required Tools
You need:
git-lfs- ImageMagick’s
magickcommand when generating derivatives locally
After cloning, pull LFS objects before you work on the site:
git lfs pull
If the LFS objects are missing, a site build may appear to work while still producing broken image references or missing derivatives.
Source of Truth
The source of truth for website media is the checked-in asset tree:
website/assets/images/for source images and generated WebPswebsite/assets/images/for source images and committed generated derivatives, including WebPs and the social preview PNGwebsite/static/files/for downloadable files
Hugo’s cached outputs under website/resources/_gen/images/ are build artifacts, not source content and not committed media.
Generated derivatives live in a sibling generated/ directory next to the source image.
Example:
website/assets/images/home/companion-screenshot.png
website/assets/images/home/generated/companion-screenshot_372w.webp
website/assets/images/home/generated/companion-screenshot_500w.webp
website/assets/images/home/generated/sambee-screenshot_1200w.png
Authoring Workflow
When you add or change a raster image:
- Add or update the source file under
website/assets/images/. - Start the website preview task or run
npm run dev. - Let the local watcher regenerate missing derivatives automatically, or generate them manually.
- Review the generated files in the matching
generated/directory. - Commit the source image and the generated derivatives together.
The local watcher covers two cases:
- For ordinary raster images, it regenerates the matching WebP derivatives.
- For
website/assets/images/home/sambee-screenshot.png, it also regenerates the committed social preview PNG atwebsite/assets/images/home/generated/sambee-screenshot_1200w.png.
Manual batch generation:
cd website
npm run images:generate
Manual social preview generation:
cd website
npm run social:generate
Do not rely on CI to create missing derivatives. Missing generated media is a defect.
Build and Publish Expectations
The website build and deployment flow is set up around this behavior:
- Local development pulls Git LFS objects before serving the site.
- The local dev workflow runs the image watcher during
npm run dev. - CI restores and pulls Git LFS objects before building the site.
- CI validates that raster source images have generated WebP derivatives.
- The shared head template fails the Hugo build if the committed social preview PNG is missing or is not
1200x630. - CI uploads
website/publicas a short-lived artifact after the build. - The deploy job publishes
website/publicto Cloudflare Pages.
The project scripts that matter most here are:
npm run images:generatenpm run images:validatenpm run social:generatenpm run build
The workflow lives in .github/workflows/website-deploy.yml. The important steps include:
- restore
.git/lfscache and rungit lfs pull - restore
website/resources/_gencache for faster rebuilds - validate generated WebP coverage
- build the Hugo site and Pagefind index inside
website/ - upload
website/publicas a short-lived artifact - deploy the built site from
website/public
For the Cloudflare-side setup, GitHub Actions secrets, and deployment branch rules, see Set Up Cloudflare Pages Publishing .