Skip to content

feat(generator): add RSS 2.0 and Atom feed generation (#80)#81

Merged
harrydayexe merged 7 commits into
mainfrom
80-rss-feeds
Jun 20, 2026
Merged

feat(generator): add RSS 2.0 and Atom feed generation (#80)#81
harrydayexe merged 7 commits into
mainfrom
80-rss-feeds

Conversation

@harrydayexe

@harrydayexe harrydayexe commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Feeds are generated automatically when a base URL is configured via WithBaseURL. Without a base URL the build succeeds and emits an info log rather than failing. Features:

  • Site-wide rss.xml and atom.xml at the output root
  • Per-tag .rss.xml and .atom.xml under tags/
  • Full post HTML content in every feed item
  • Post URL used as the item's unique identifier (guid/id)
  • Configurable post limit (default 10, newest-first)
  • WithDisableFeeds() to opt out entirely
  • FeedsEnabled flag on BaseData controls template discovery links and visible RSS nav link in the default theme
  • New config options: WithBaseURL, WithDisableFeeds, WithFeedPostLimit
  • CLI flags: --base-url, --disable-feeds, --feed-limit

Uses github.com/gorilla/feeds for RSS/Atom serialisation.

Closes #80

Changelog (#81)

✨ New Features

🐛 Bug Fixes

  • (generator) set feed to max effective-updated across posts
  • (generator) make feed-limit 0 unlimited and clarify base-url scope

♻️ Refactoring

  • (generator) make feed building private methods on Generator
  • (generator) eliminate redundant title parameter from feed methods
  • (generator) unexport absURL, fix feed-limit CLI default

Feeds are generated automatically when a base URL is configured via
WithBaseURL. Without a base URL the build succeeds and emits an info
log rather than failing. Features:

- Site-wide rss.xml and atom.xml at the output root
- Per-tag <tag>.rss.xml and <tag>.atom.xml under tags/
- Full post HTML content in every feed item
- Post URL used as the item's unique identifier (guid/id)
- Configurable post limit (default 10, newest-first)
- WithDisableFeeds() to opt out entirely
- FeedsEnabled flag on BaseData controls template discovery links
  and visible RSS nav link in the default theme
- New config options: WithBaseURL, WithDisableFeeds, WithFeedPostLimit
- CLI flags: --base-url, --disable-feeds, --feed-limit

Uses github.com/gorilla/feeds for RSS/Atom serialisation.

Closes #80
Feed logic now lives as unexported methods on *Generator, pulling
limit, title, base URL, and blog root directly from the receiver
rather than requiring callers to pass them explicitly. AbsURL remains
exported as a standalone utility. Call sites in assembleBlogWithTemplates
simplify to g.buildFeeds(posts) and g.buildFeedsWithTitle(posts, title).
…thods

Replace buildFeedsWithTitle(posts, title) with two intent-named methods:
buildFeeds for the site-wide feed and buildTagFeeds(tag, posts) for
per-tag feeds. Both derive the feed title from the generator's own
SiteTitle, removing a parameter that duplicated information already
available on the receiver.
The feed-level <updated> (Atom) and <lastBuildDate> (RSS) were always
set to posts[0].Date — the newest post's publication date. Per RFC 4287
§4.2.15 the feed's <updated> must reflect the most recent instant any
entry was significantly modified. Editing an older post without
publishing a new one left the timestamp unchanged, so aggregators and
caches could miss the edit entirely.

Fix computes the maximum effective-updated time across all windowed
posts (LastEdited if set, otherwise Date) and uses that for
feed.Updated; feed.Created remains posts[0].Date. Per-item timestamps
were already correct and are unchanged.

Adds three tests for the previously zero-coverage LastEdited paths:
feed-level updated reflects an edit, item Atom <updated> reflects
LastEdited, and regression guard when no edits are present.
- Rename AbsURL → absURL; it has no external consumers and is an
  internal helper for feed URL construction.
- Set --feed-limit flag default to 10 (matching the generator's
  internal default) so the CLI help text displays the correct value
  rather than 0.
- Remove dead code in TestGenerator_FeedPostLimit: an abandoned
  postTemplate const and postsMap loop that were superseded by an
  inline fstest.MapFS but never deleted.
…serve

Move template-dir, root-path, disable-tags, disable-reading-time,
base-url, disable-feeds, and feed-limit from per-subcommand flag
definitions to the top-level goblog command via a new internal/cliflags
package. urfave/cli v3 persists root flags to all subcommands, so both
generate and serve share one canonical definition with no duplication.

Wire the three feed options (base-url, disable-feeds, feed-limit) into
the serve command's generator config so the embedded generator produces
RSS/Atom content. Extend pkg/server/handler to expose that content at
{root}rss.xml, {root}atom.xml, and {root}tags/{tag}.{rss,atom}.xml,
matching the static file layout written by pkg/outputter. Feed routes
return 404 when no base-url is configured.
Remove the library-side 0→10 coercion in generator.New so that
WithFeedPostLimit(0) (and the unset default) now means unlimited.
The 10-post default is retained in the CLI flag value; both generate
and serve subcommands now pass the flag value unconditionally.

Clarify in godoc and README that WithBaseURL must be scheme+host only
— combining a path here with WithBlogRoot produces doubled segments in
every feed URL.

Also extract effectiveUpdated() helper to deduplicate the
LastEdited-vs-Date logic, expand the handleTag comment to explain why
feed demuxing lives inside the wildcard handler, and fix a stale
AbsURL→absURL error message in the test.
@harrydayexe harrydayexe merged commit 3d8f190 into main Jun 20, 2026
5 checks passed
@harrydayexe harrydayexe deleted the 80-rss-feeds branch June 20, 2026 20:37
@harrydayexe harrydayexe added this to the v2.6.0 milestone Jun 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add RSS & Atom feed support for blogs

1 participant