Serve blog images via wsrv.nl CDN proxy in production#285
Conversation
Fixes GitHub Pages artifact exceeding 1GB limit (was 1.28GB). In production, image templates emit wsrv.nl proxy URLs instead of Hugo-processed local paths, reducing artifact from 2GB to 119MB. - CDN disabled by default, enabled in config/production/hugo.toml - Templates check site.Params.cdn.enabled (per-environment config) - Responsive images preserved: picture/srcset with webp+jpeg at 4 widths - Blog thumbnails, OG images, shortcode images all CDN-ified - GitHub Actions strips blog media after build - Dev mode unchanged (hugo server works as before) - Cloudflare R2 option documented for future use Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 33 minutes and 54 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (23)
📝 WalkthroughWalkthroughAdds a conditional CDN image-proxy flow (wsrv.nl) enabled for production, updates templates to emit CDN-backed image URLs when enabled, adds a CDN URL partial and tests, and changes CI/workflow to build with newer Hugo, remove blog media from the artifact, and log artifact size before upload. Changes
Sequence Diagram(s)sequenceDiagram
participant CI as GitHub Actions
participant Hugo as Hugo Build
participant Templates as Site Templates
participant CDN as wsrv.nl
participant Artifact as Pages Artifact
CI->>Hugo: run build (environment=production, params.cdn.enabled=true)
Hugo->>Templates: render pages (templates consult site.Params.cdn)
Templates->>CDN: construct wsrv.nl URLs via `cdn/url` partial (rawBase + resource path + params)
Hugo->>Artifact: write `public/` (includes CDN-backed URLs, no local blog media removed yet)
CI->>Artifact: delete image/media from `public/blog/` (post-build)
CI->>CI: measure `public/` size
CI->>GitHub Pages: upload artifact & deploy
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
test/unit/cdn_image_proxy_test.rb (1)
62-65: Prefer behavior assertions over repository-specific config assertions.Asserting host/repo strings couples the test to current config internals instead of CDN behavior. This will create unnecessary failures if
rawBasechanges while feature behavior remains correct.💡 Suggested direction
- assert_includes srcset, "raw.githubusercontent.com", - "CDN URLs should proxy from GitHub raw content" - assert_includes srcset, "jetthoughts/jetthoughts.github.io", - "CDN URLs should reference this repository" + assert_includes srcset, "wsrv.nl", "CDN URLs should use wsrv proxy host" + assert_match(/url=.*\/content\//, srcset, "CDN URLs should proxy source content path")Based on learnings: Ensure test quality by focusing on behavior only; reject implementation/existence/config tests as defined in
/knowledge/25.04-test-smell-prevention-enforcement-protocols.md.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/unit/cdn_image_proxy_test.rb` around lines 62 - 65, The assertions in cdn_image_proxy_test.rb that check for repository-specific strings ("raw.githubusercontent.com" and "jetthoughts/jetthoughts.github.io") couple the test to config; instead, update the assertions that reference srcset to verify CDN behavior only: remove the two repo-specific assert_includes checks and replace them with behavior-focused assertions on srcset (e.g., assert that each URL is rewritten/proxied by the CDN pattern or matches a proxy/hostname-regex and/or that original upstream hostnames are not present). Target the srcset variable in the test and use a generic proxy-pattern match or existence/absence checks rather than repository-specific literals.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@config/_default/hugo.toml`:
- Around line 56-60: The templates currently hardcode wsrv.nl, so the new
params.cdn.provider knob does nothing; update the image/SEO templates
(_shortcodes/img.html, layouts/partials/blog/img-cropped.html,
_markup/render-image.html and the SEO partials) to read
.Site.Params.cdn.provider and branch on its value (e.g., "wsrv" vs
"cloudflare"), building URLs from .Site.Params.cdn.rawBase for wsrv and from
.Site.Params.cdn.cloudflareBase for Cloudflare R2; ensure the same branching
logic is used wherever image URLs are generated or proxied, or if you prefer to
keep changes minimal remove the unused provider/cloudflare keys from params.cdn
instead of changing templates.
In `@docs/workflows/cdn-image-proxy.md`:
- Around line 45-47: Add a language identifier to the fenced code block that
contains the URL pattern (the block starting with ``` and containing
"https://wsrv.nl/?url=raw.githubusercontent.com/OWNER/REPO/BRANCH/content/PATH&w=WIDTH&output=FORMAT&q=QUALITY");
change the opening fence from ``` to ```text so the block becomes ```text ...
``` to satisfy markdownlint MD040.
In `@layouts/partials/seo/enhanced-meta-tags.html`:
- Around line 109-111: The CDN branch currently builds wsrv.nl URLs with
fit=inside (see the site.Params.cdn.enabled block and variables $rawURL and
$image) but still emits hardcoded og:image:width="1200" and
og:image:height="630"; either change the wsrv.nl query to request an exact
output (e.g., replace fit=inside with fit=cover or fit=fill so the image will be
1200x630) or compute and emit the actual output dimensions instead of the
hardcoded 1200/630 values; update every CDN occurrence (the $image construction
and the corresponding og:image:width/og:image:height outputs referenced in the
same template branches) so the URL and the published width/height are
consistent.
In `@test/unit/cdn_image_proxy_test.rb`:
- Around line 11-12: The conditional skip ("skip \"No blog posts found\" if
`@blog_posts.empty`?") masks regressions; remove that skip and make the test
deterministic by ensuring required data exists (e.g., create or load a blog post
with an og/picture before exercising the CDN proxy) or explicitly assert
expected behavior when no posts exist; update setup that populates `@blog_posts`
(or replace `@blog_posts` with a factory/fixture creation step) so the test always
exercises the CDN image proxy logic rather than silently passing when
`@blog_posts` is empty.
In `@themes/beaver/layouts/partials/seo/enhanced-meta-tags.html`:
- Around line 213-217: The CDN branch currently hardcodes $imageWidth="1200" and
$imageHeight="630" which can be inaccurate because wsrv.nl may return smaller
images; update the logic in the site.Params.cdn.enabled branch to either derive
real dimensions from the Hugo resource (use the page resource's .Width and
.Height for the file referenced by $resource or $page.File/ resource) and set
$imageWidth/$imageHeight from those values, or remove the og:image:width and
og:image:height meta tags entirely for the CDN case; apply the same change to
the non-CDN image branch that sets $imageWidth/$imageHeight (the other block
around lines 232–236) so both branches use real resource dimensions or omit the
width/height assertions.
---
Nitpick comments:
In `@test/unit/cdn_image_proxy_test.rb`:
- Around line 62-65: The assertions in cdn_image_proxy_test.rb that check for
repository-specific strings ("raw.githubusercontent.com" and
"jetthoughts/jetthoughts.github.io") couple the test to config; instead, update
the assertions that reference srcset to verify CDN behavior only: remove the two
repo-specific assert_includes checks and replace them with behavior-focused
assertions on srcset (e.g., assert that each URL is rewritten/proxied by the CDN
pattern or matches a proxy/hostname-regex and/or that original upstream
hostnames are not present). Target the srcset variable in the test and use a
generic proxy-pattern match or existence/absence checks rather than
repository-specific literals.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: c1a9d72d-b20d-42f6-88d7-1894c273d46f
📒 Files selected for processing (10)
.github/workflows/_hugo.ymlconfig/_default/hugo.tomlconfig/production/hugo.tomldocs/workflows/cdn-image-proxy.mdlayouts/partials/seo/enhanced-meta-tags.htmltest/unit/cdn_image_proxy_test.rbthemes/beaver/layouts/_markup/render-image.htmlthemes/beaver/layouts/_shortcodes/img.htmlthemes/beaver/layouts/partials/blog/img-cropped.htmlthemes/beaver/layouts/partials/seo/enhanced-meta-tags.html
fc09a77 to
afb0ce2
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (2)
docs/workflows/cdn-image-proxy.md (1)
32-35: Add a language identifier to the fenced code block.The code block is missing a language specifier, triggering markdownlint MD040.
💡 Suggested fix
-``` +```text {{ partial "cdn/url" (dict "page" $page "resource" $resource "params" "w=400&output=webp&q=80") }} → "https://wsrv.nl/?url=raw.githubusercontent.com/.../image.jpg&w=400&output=webp&q=80"</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@docs/workflows/cdn-image-proxy.mdaround lines 32 - 35, Add a language
identifier (for example "text") to the fenced code block that contains the {{
partial "cdn/url" (dict "page" $page "resource" $resource "params"
"w=400&output=webp&q=80") }} snippet and the arrowed example so markdownlint
MD040 is resolved; update the opening fence fromtotext and keep the
closing fence as ``` with no other content changes.</details> </blockquote></details> <details> <summary>test/unit/cdn_image_proxy_test.rb (1)</summary><blockquote> `55-68`: **Consider tightening the thumbnail selector if it becomes flaky.** The CSS selector `.blog-post img, .post-image img, article img` is broad. If the blog listing markup changes, this test may need adjustment. The current approach is pragmatic for initial coverage. <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@test/unit/cdn_image_proxy_test.rb` around lines 55 - 68, The selector used in test_blog_thumbnails_use_cdn (".blog-post img, .post-image img, article img") is too broad and may catch unrelated images; narrow it to target the actual listing thumbnail elements (e.g. a more specific selector within the listing container such as ".blog-listing .blog-post .thumbnail img" or a data-attribute like "[data-role=\"thumbnail\"] img") so the test only inspects thumbnail images — update the selector string in test_blog_thumbnails_use_cdn to a more specific CSS path that matches your current listing markup (or add/target a stable data attribute) and keep the same assertions for wsrv.nl, output=jpg and q=90. ``` </details> </blockquote></details> </blockquote></details> <details> <summary>🤖 Prompt for all review comments with AI agents</summary>Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In@docs/workflows/cdn-image-proxy.md:
- Around line 32-35: Add a language identifier (for example "text") to the
fenced code block that contains the {{ partial "cdn/url" (dict "page" $page
"resource" $resource "params" "w=400&output=webp&q=80") }} snippet and the
arrowed example so markdownlint MD040 is resolved; update the opening fence from
totext and keep the closing fence as ``` with no other content changes.In
@test/unit/cdn_image_proxy_test.rb:
- Around line 55-68: The selector used in test_blog_thumbnails_use_cdn
(".blog-post img, .post-image img, article img") is too broad and may catch
unrelated images; narrow it to target the actual listing thumbnail elements
(e.g. a more specific selector within the listing container such as
".blog-listing .blog-post .thumbnail img" or a data-attribute like
"[data-role="thumbnail"] img") so the test only inspects thumbnail images —
update the selector string in test_blog_thumbnails_use_cdn to a more specific
CSS path that matches your current listing markup (or add/target a stable data
attribute) and keep the same assertions for wsrv.nl, output=jpg and q=90.</details> --- <details> <summary>ℹ️ Review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: Path: .coderabbit.yaml **Review profile**: CHILL **Plan**: Pro **Run ID**: `da5da7cc-2e94-4c09-8ce1-3cf29d2808f5` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between 57bd0c2871efd52e7d95646af8fd92e8aa6357bf and b630be6aa502a7cef168f691aa55a4de19edf051. </details> <details> <summary>📒 Files selected for processing (21)</summary> * `.github/actions/setup-hugo/action.yml` * `.github/workflows/_hugo.yml` * `.github/workflows/test.yml` * `bin/hugo-build` * `config/_default/hugo.toml` * `docs/workflows/cdn-image-proxy.md` * `layouts/partials/seo/enhanced-meta-tags.html` * `test/unit/cdn_image_proxy_test.rb` * `themes/beaver/layouts/_markup/render-image.html` * `themes/beaver/layouts/_shortcodes/img.html` * `themes/beaver/layouts/partials/blog/img-cropped.html` * `themes/beaver/layouts/partials/blog/json-ld.html` * `themes/beaver/layouts/partials/cdn/url.html` * `themes/beaver/layouts/partials/components/testimonial.html` * `themes/beaver/layouts/partials/data/authors-cached.html` * `themes/beaver/layouts/partials/data/company-cached.html` * `themes/beaver/layouts/partials/data/testimonials-cached.html` * `themes/beaver/layouts/partials/homepage/companies.html` * `themes/beaver/layouts/partials/page/testimonials.html` * `themes/beaver/layouts/partials/seo/enhanced-meta-tags.html` * `themes/beaver/layouts/partials/technologies.html` </details> <details> <summary>🚧 Files skipped from review as they are similar to previous changes (6)</summary> * themes/beaver/layouts/_shortcodes/img.html * config/_default/hugo.toml * themes/beaver/layouts/partials/seo/enhanced-meta-tags.html * themes/beaver/layouts/partials/blog/img-cropped.html * .github/workflows/_hugo.yml * layouts/partials/seo/enhanced-meta-tags.html </details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
8e76534 to
6cbfa91
Compare
Build once, test with precompiled output: - Hugo build in _hugo.yml uploads artifact, unit tests download it - Unit tests moved from test.yml to publish.yml (needs: build) - test.yml now only handles manual screenshot tests - PRECOMPILED_ASSETS skips redundant Hugo rebuild in test helper Hugo & actions upgrade: - Hugo 0.149.1 → 0.160.1 (CI workflow + setup-hugo action) - Migrate site.Data → hugo.Data (8 files, fixes deprecation warning) - configure-pages v5→v6, upload/deploy-pages v4→v5 - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 for peaceiris/actions-hugo v3 CDN cleanup from review: - Extract shared partials/cdn/url.html (was 10+ copies) - Fix trailing & in CDN URL when params empty - Fix duplicate twitter:card meta tag - Fix OG image fit=inside → fit=cover - Remove SVG/mp4 from blog media cleanup - Add preconnect hint for wsrv.nl - bin/hugo-build: accept OUTPUT_DIR and BASE_URL env vars Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6cbfa91 to
25b1f07
Compare
The single.html cover image block was missed when CDN proxy support was added in PR #285, causing 404s for all blog post cover images on production (CI deletes processed images from public/blog/). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The single.html cover image block was missed when CDN proxy support was added in PR #285, causing 404s for all blog post cover images on production (CI deletes processed images from public/blog/). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
config/_default/), enabled only inconfig/production/hugo.tomlhugo server) unchanged — local image processing as beforeChanges
[params.cdn]withenabled,provider,rawBase— toggled per environmentrender-image.html: CDN branch emits<picture>srcset via wsrv.nl (webp+jpeg, 400/800/1200/1600w)img-cropped.html: Blog list thumbnails via wsrv.nl (retina JPG)enhanced-meta-tags.html: OG images via wsrv.nl (both rootlayouts/andthemes/beaver/copies)img.htmlshortcode: Direct images via wsrv.nl_hugo.yml: Strips blog media frompublic/blog/after build, before artifact uploaddocs/workflows/cdn-image-proxy.mddocuments both wsrv.nl and Cloudflare R2 solutionsImpact
Test plan
bin/rake test:critical— 42 tests, 116 assertions, 0 failureshugo --environment development) — still uses local image processing🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Improvements
Documentation
Tests