fix(www): sitemap lists all changelog slugs (#50275)

The www sitemap generator reads changelog URLs from the build-generated
RSS feed but only accepted links whose slug starts with a number, the
shape `computeChangelogEntrySlug` produces solely for entries carrying
`legacy_gh_discussion`. Every changelog entry authored since that
migration has a plain text slug and was silently missing from
`sitemap_www.xml`. I widened the link match to any non-empty slug; the
RSS builder is the only producer of that file and emits exactly one link
per item, so no other filter is needed.

I added a text-slug RSS item to the fixture-driven sitemap test and
asserted that the changelog URL list equals the RSS item list, so a
future filter that drops entries fails the suite.

**Note:** text-slug entries now pass through the same fail-the-build
pubDate check that numeric-prefixed entries already did after #50198. A
changelog entry with no `publish_date` and no date-prefixed filename
would produce an unparseable pubDate and stop the www build. The
alternative, shipping the URL without lastmod, is a one-line change. I
kept the gate because every current changelog entry carries a
date-prefixed filename, the changelog repo documents that convention,
and the build error names the entry URL.

## To test

Tested on Vercel preview:
- [x] Count `<item>` blocks in `<preview>/changelog-rss.xml`, then count
`/changelog/` locs in `<preview>/sitemap_www.xml`, expect the two counts
to match
- [x] Search the preview sitemap for `/changelog/pipelines`, expect one
`<loc>` entry with a `<lastmod>` date
- [x] Search the preview sitemap for a numeric-prefixed entry such as
`/changelog/47796-developer-update-july-2026`, expect it still present

## Linear
- fixes GROWTH-1212


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **Bug Fixes**
- Changelog pages with text-based slugs are now correctly recognized in
the sitemap.
- Sitemap entries for these changelog pages now use their RSS
publication dates.
- RSS links are matched more reliably, ensuring all valid changelog URLs
are included.
- Invalid publication dates are rejected instead of producing incorrect
sitemap metadata.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Pamela Chia authored and GitHub committed 2026-09-12 02:11:25 +08:00
1 parent ea203f70df
commit f012dfa850
2 files changed
+30 -4

No files matched your search

+29 -3
View File
@@ -10,6 +10,7 @@ import { blogPostingSchema, serializeJsonLd } from './lib/json-ld'
const GENERATOR = path.join(process.cwd(), 'internals', 'generate-sitemap.mjs')
const LEGACY_LINK = 'https://supabase.com/changelog/12345-legacy-entry'
const TIMED_LINK = 'https://supabase.com/changelog/23456-timed-entry'
const TEXT_SLUG_LINK = 'https://supabase.com/changelog/text-slug-entry'
const SPAWN_TIMEOUT_MS = 30_000
const createdDirs: string[] = []
@@ -57,7 +58,15 @@ function rssItem(link: string, pubDate: string): string {
}
function rss(items: string[]): string {
return `<?xml version="1.0" encoding="UTF-8"?>\n<rss version="2.0"><channel>\n${items.join('\n')}\n</channel></rss>\n`
return [
'<?xml version="1.0" encoding="UTF-8"?>',
'<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel>',
'<link>https://supabase.com/changelog</link>',
'<atom:link href="https://supabase.com/changelog-rss.xml" rel="self" type="application/rss+xml"/>',
...items,
'</channel></rss>',
'',
].join('\n')
}
function urlEntries(xml: string): UrlEntry[] {
@@ -91,8 +100,9 @@ describe('generate-sitemap lastmod', () => {
'_events/2026-02-01-webinar.mdx': mdx("date: '2026-02-01T19:00:00.000-07:00'"),
'pages/company.tsx': '',
'public/changelog-rss.xml': rss([
rssItem(LEGACY_LINK, 'Tue, 03 Feb 2026 00:00:00 +0000'),
rssItem(TIMED_LINK, 'Wed, 04 Feb 2026 20:15:00 -0700'),
rssItem(TEXT_SLUG_LINK, 'Thu, 05 Feb 2026 00:00:00 +0000'),
rssItem(LEGACY_LINK, 'Tue, 03 Feb 2026 00:00:00 +0000'),
]),
})
result = runGenerator(fixtureDir)
@@ -155,9 +165,20 @@ describe('generate-sitemap lastmod', () => {
expect(entryFor(TIMED_LINK)?.lastmod).toBe('2026-02-05')
})
it('includes changelog entries whose slug has no numeric prefix', () => {
expect(entryFor(TEXT_SLUG_LINK)?.lastmod).toBe('2026-02-05')
})
it('emits exactly the RSS item links as changelog URLs', () => {
const changelogLocs = entries
.filter((entry) => entry.loc.startsWith('https://supabase.com/changelog'))
.map((entry) => entry.loc)
expect(changelogLocs).toEqual([TIMED_LINK, TEXT_SLUG_LINK, LEGACY_LINK])
})
it('emits only day-precision lastmod values, one per dated source', () => {
const lastmods = entries.map((entry) => entry.lastmod).filter(Boolean)
expect(lastmods).toHaveLength(9)
expect(lastmods).toHaveLength(10)
for (const lastmod of lastmods) expect(lastmod).toMatch(/^\d{4}-\d{2}-\d{2}$/)
})
@@ -267,6 +288,11 @@ describe('generate-sitemap rejects dates it cannot trust', () => {
files: { 'public/changelog-rss.xml': rss([rssItem(LEGACY_LINK, 'Invalid Date +0000')]) },
stderrIncludes: [`changelog-rss ${LEGACY_LINK}`, '"Invalid Date +0000"'],
},
{
name: 'an unparseable changelog pubDate on a text-slug entry',
files: { 'public/changelog-rss.xml': rss([rssItem(TEXT_SLUG_LINK, 'Invalid Date +0000')]) },
stderrIncludes: [`changelog-rss ${TEXT_SLUG_LINK}`, '"Invalid Date +0000"'],
},
]
for (const testCase of cases) {
+1 -1
View File
@@ -180,7 +180,7 @@ async function generate() {
const lastmodByUrl = new Map()
for (const [, item] of rss.matchAll(/<item>([\s\S]*?)<\/item>/g)) {
const link = item.match(/<link>(https:\/\/supabase\.com\/changelog\/\d+[^<]*)<\/link>/)?.[1]
const link = item.match(/<link>(https:\/\/supabase\.com\/changelog\/[^<]+)<\/link>/)?.[1]
if (!link || lastmodByUrl.has(link)) continue
const pubDate = item.match(/<pubDate>([^<]*)<\/pubDate>/)?.[1]
lastmodByUrl.set(link, pubDate ? changelogLastmod(pubDate, link) : undefined)