Skip to content

Generate sitemap lastmod dates from git history - #20

Merged
AlexanderWagnerDev merged 4 commits into
mainfrom
claude/project-thread-ujsrjg
Sep 18, 2026
Merged

AlexanderWagnerDev merged 4 commits into
mainfrom
claude/project-thread-ujsrjg

Conversation

@claude

@claude claude Bot commented Sep 18, 2026

Copy link
Copy Markdown

Requested by Alex · project thread

Before: sitemap.xml was maintained by hand. Its lastmod dates said 2026-07-18 or 2026-08-09 while every page had actually been edited on 2026-09-07, so crawlers were told the site was one to two months staler than it is. /legal/ carried no lastmod at all. Nothing kept the file honest, so the next content change would have put it back out of date.

After: sitemap.xml is generated. Every URL's lastmod is the date of the most recent commit that touched that page's index.php, /legal/ has one, and CI fails if the committed file stops matching git history.

Eleven of the twelve dates come out as 2026-09-07, because commit 2fa9097 ("Fix SonarCloud code findings") touched all twelve pages that day. The twelfth, /guides/rtmp-server-ha-clustering/, reads 2026-09-18 after #19 landed — which is the mechanism doing exactly its job. The rest will spread apart the same way as pages get edited individually.

How

scripts/generate-sitemap.sh reads each date from git log -1 --format=%cs -- <page> and writes the whole file. The URL list, changefreq, and priority stay explicit in a PAGES table at the top of the script rather than being inferred, so crawl hints remain a deliberate choice. Two guards keep it from lying quietly: it refuses to run in a shallow clone, where commit dates would be wrong, and it fails if an index.php exists on disk but is missing from the table — the drift that step 5 of "Adding a guide" used to invite.

Both existing workflows now use it, and both checkouts move to fetch-depth: 0 so the history is actually there:

  • Website checks regenerates into a temp file and diffs it against the committed sitemap.xml, failing with the command to run. The existing xmllint validation is untouched.
  • Deploy to production regenerates in place immediately before the SFTP upload, so the live sitemap is correct even if the committed copy ever lags. scripts/ is added to the deploy exclude list so the script itself is not published to the web root.

The drift check hard-fails, which means a PR that edits a page also needs its regenerated sitemap.xml committed. That is stable under this repo's merge-commit history, since a merge preserves the original commit date of the file. If the repo ever switches to squash merges, a squash landing on a different day than the PR commit would shift the date and need one scripts/generate-sitemap.sh run on main.

One suppression worth a look

SonarCloud failed the quality gate on S5332, "using clear-text protocols", pointing at the http:// in xmlns="http://www.sitemaps.org/schemas/sitemap/0.9".

That one cannot be changed. The sitemap protocol defines the namespace as that literal string; it is an identifier that is never dereferenced, and crawlers match it byte for byte, so an https variant would simply not be a sitemap namespace. The same URI is already in the committed sitemap.xml and always has been — the warning only appeared because the string now also lives in a shell script, where Sonar's generic text scan sees it.

It is suppressed with NOSONAR and a comment explaining why. Marking that hotspot "Safe" in the SonarCloud UI instead, and dropping the NOSONAR, would be the tidier long-term answer if you would rather keep suppressions out of the source.

Verified

  • scripts/generate-sitemap.sh output is byte-identical to the committed sitemap.xml (the CI drift check, run locally)
  • xmllint --noout sitemap.xml passes
  • shellcheck -s sh scripts/generate-sitemap.sh is clean
  • find . -name '*.php' | xargs -n1 php -l passes
  • Shallow-clone guard exits 1 against a --depth 1 clone
  • Missing-page guard exits 1 when an untracked guides/fake-page/index.php is added
  • Runs correctly when invoked from a subdirectory
  • Generated XML is unchanged by the NOSONAR comment

🤖 Generated with Claude Code

https://claude.ai/code/session_01NTJ2WWC1QQxAmMeu75hvbT

The lastmod dates in sitemap.xml were maintained by hand and had drifted
months behind the pages they describe, and /legal/ carried no lastmod at
all.

Add scripts/generate-sitemap.sh, which takes each URL's lastmod from the
committer date of the most recent commit touching that page's index.php.
The URL list, changefreq, and priority stay explicit in a PAGES table at
the top of the script; the script fails if a page exists on disk but is
missing from that table, and refuses to run against a shallow clone
rather than silently emitting wrong dates.

Wire it into both existing workflows: Website checks now fails when the
committed sitemap.xml differs from what the script produces, and the
production deploy regenerates it just before the SFTP upload. Both
checkouts move to fetch-depth 0 so commit dates are available, and the
deploy excludes scripts/ from the upload.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTJ2WWC1QQxAmMeu75hvbT
@coderabbitai

coderabbitai Bot commented Sep 18, 2026

Copy link
Copy Markdown

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 302c6472-57b0-45a2-8363-90241ed2dc79

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@claude
claude Bot requested a review from AlexanderWagnerDev September 18, 2026 09:35
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTJ2WWC1QQxAmMeu75hvbT
SonarCloud's quality gate flagged a security issue on the new code. Both
temp paths the change introduced were predictable and therefore open to
a symlink attack: sitemap.xml.tmp.$PID in the generator, and a hardcoded
/tmp/sitemap.expected.xml in the workflow. Both now use mktemp.

mktemp creates the file 0600, so the generator restores mode 644 before
moving it into place - sitemap.xml is served publicly.

Also inline the usage text rather than sed-ing it back out of $0, and
regenerate sitemap.xml after merging main: PR #19 edited the HA
clustering guide, so that page's lastmod is now 2026-09-18.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTJ2WWC1QQxAmMeu75hvbT
SonarCloud's quality gate failed on a single finding: S5332 "Make sure
that using clear-text protocols is safe here" on the http:// in the
urlset namespace declaration.

That one has to stay as it is. The sitemap protocol defines the
namespace as the literal string
http://www.sitemaps.org/schemas/sitemap/0.9; it is an identifier that is
never dereferenced, and crawlers match it byte for byte, so an https
variant would simply not be a sitemap namespace. The same URI is already
in the committed sitemap.xml - the warning appeared only because the
string now also lives in a shell script.

Suppress it on that line with NOSONAR and say why in a comment. The
generated XML is byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NTJ2WWC1QQxAmMeu75hvbT
@sonarqubecloud

Copy link
Copy Markdown

@AlexanderWagnerDev
AlexanderWagnerDev marked this pull request as ready for review September 18, 2026 09:51
@AlexanderWagnerDev
AlexanderWagnerDev merged commit 23ce796 into main Sep 18, 2026
7 checks passed
@AlexanderWagnerDev
AlexanderWagnerDev deleted the claude/project-thread-ujsrjg branch September 18, 2026 09:51
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

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.

2 participants