diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 979bc17..15738cc 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -22,6 +22,11 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: persist-credentials: false + # Full history: sitemap values come from commit dates. + fetch-depth: 0 + + - name: Regenerate sitemap.xml from git history + run: scripts/generate-sitemap.sh - name: Deploy over SFTP uses: wangyucode/sftp-upload-action@a1e64f60df0d085b26ca9efff77566e79f1c6b78 # v3.0.0 @@ -32,4 +37,4 @@ jobs: password: ${{ secrets.DEPLOY_PASSWORD }} localDir: '.' remoteDir: ${{ secrets.DEPLOY_REMOTE_PATH }} - exclude: '.git/*,.github/*,README.md,AGENTS.md,renovate.json' + exclude: '.git/*,.github/*,scripts/*,README.md,AGENTS.md,renovate.json' diff --git a/.github/workflows/site-checks.yml b/.github/workflows/site-checks.yml index 6648c13..7bc76d0 100644 --- a/.github/workflows/site-checks.yml +++ b/.github/workflows/site-checks.yml @@ -17,6 +17,8 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: persist-credentials: false + # Full history: sitemap values come from commit dates. + fetch-depth: 0 - name: Set up PHP uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2 @@ -33,6 +35,16 @@ jobs: sudo apt-get install -y libxml2-utils xmllint --noout sitemap.xml + - name: Check sitemap matches git history + run: | + expected=$(mktemp) + scripts/generate-sitemap.sh -o "$expected" + if ! diff -u sitemap.xml "$expected"; then + echo + echo "sitemap.xml is out of date. Run scripts/generate-sitemap.sh and commit the result." + exit 1 + fi + - name: Smoke-test public pages run: | php -S 127.0.0.1:8090 >/tmp/openrtmp-site.log 2>&1 & diff --git a/README.md b/README.md index 3eb90fb..2539a3f 100644 --- a/README.md +++ b/README.md @@ -49,9 +49,30 @@ assets/css/content.css Article and quickstart styles assets/js/ Navigation, copy, and docs behavior assets/img/ Logo and favicon robots.txt Crawler policy -sitemap.xml Indexable public pages +sitemap.xml Indexable public pages (generated) +scripts/generate-sitemap.sh Rebuilds sitemap.xml from git history ``` +## Sitemap + +`sitemap.xml` is generated, not hand-edited. Each `` is the date of the +most recent commit that touched that page's `index.php`, so the sitemap cannot +drift away from the content it describes. + +```bash +scripts/generate-sitemap.sh # rewrite sitemap.xml +scripts/generate-sitemap.sh -o - # preview on stdout +``` + +The URL list, `changefreq`, and `priority` live in the `PAGES` table at the top +of the script. The script fails if an `index.php` exists on disk but is missing +from that table, and it needs full git history — a shallow clone is rejected +rather than silently producing wrong dates. + +CI enforces both ends of this: `Website checks` fails when the committed +`sitemap.xml` differs from what the script produces, and the production deploy +regenerates it just before upload. + ## Content principles - Separate the **developer/library** path from the **operator/server** path. @@ -68,7 +89,8 @@ sitemap.xml Indexable public pages 2. Set a unique `$pageTitle`, `$pageDescription`, and `$canonicalPath`. 3. Add `TechArticle` structured data when appropriate. 4. Link the guide from `guides/index.php` and relevant existing pages. -5. Add the canonical URL to `sitemap.xml`. +5. Add the page to the `PAGES` table in `scripts/generate-sitemap.sh`, then run + `scripts/generate-sitemap.sh` and commit the regenerated `sitemap.xml`. 6. Run PHP lint and review mobile table/code overflow. ## Deployment diff --git a/scripts/generate-sitemap.sh b/scripts/generate-sitemap.sh new file mode 100755 index 0000000..0b74da3 --- /dev/null +++ b/scripts/generate-sitemap.sh @@ -0,0 +1,121 @@ +#!/bin/sh +# Generate sitemap.xml with taken from each page's git history. +# +# The lastmod of a URL is the committer date of the most recent commit that +# touched the page's index.php, so the sitemap cannot drift away from the +# content it describes. +# +# Usage: +# scripts/generate-sitemap.sh # rewrite sitemap.xml in place +# scripts/generate-sitemap.sh -o FILE # write to FILE ('-' for stdout) + +set -eu + +BASE_URL='https://openrtmp.org' + +# One row per indexable page: URL path, source file, changefreq, priority. +# Adding a page here is the only step needed to get it into the sitemap; the +# coverage check below fails if a page exists on disk but is missing from this +# table. +PAGES=' +/|index.php|weekly|1.0 +/quickstart/|quickstart/index.php|monthly|0.9 +/guides/|guides/index.php|weekly|0.8 +/guides/self-hosted-rtmp-server-docker/|guides/self-hosted-rtmp-server-docker/index.php|monthly|0.8 +/guides/rtmps-server-obs/|guides/rtmps-server-obs/index.php|monthly|0.8 +/guides/rtmp-server-ha-clustering/|guides/rtmp-server-ha-clustering/index.php|monthly|0.8 +/guides/enhanced-rtmp-hevc-av1-opus/|guides/enhanced-rtmp-hevc-av1-opus/index.php|monthly|0.8 +/guides/openrtmp-noalbs-json-stats/|guides/openrtmp-noalbs-json-stats/index.php|monthly|0.8 +/guides/openrtmp-vs-nginx-rtmp/|guides/openrtmp-vs-nginx-rtmp/index.php|monthly|0.8 +/docs/|docs/index.php|weekly|0.8 +/download/|download/index.php|weekly|0.8 +/legal/|legal/index.php|yearly|0.2 +' + +output='sitemap.xml' +while [ $# -gt 0 ]; do + case "$1" in + -o|--output) output="${2:?-o needs a file}"; shift 2 ;; + -h|--help) + echo 'Usage: generate-sitemap.sh [-o FILE]' + echo + echo "Rewrite sitemap.xml with each URL's taken from the date of" + echo "the most recent commit touching that page. Use '-o -' for stdout." + exit 0 ;; + *) echo "generate-sitemap: unknown argument '$1'" >&2; exit 2 ;; + esac +done + +repo_root=$(git rev-parse --show-toplevel) +cd "$repo_root" + +# lastmod is read from commit history, so a shallow checkout would silently +# produce wrong dates. Fail loudly instead. +if [ "$(git rev-parse --is-shallow-repository)" = 'true' ]; then + echo "generate-sitemap: shallow clone - full history is required" >&2 + echo " In GitHub Actions, check out with 'fetch-depth: 0'." >&2 + exit 1 +fi + +# Every page on disk must appear in PAGES. +missing='' +for page in $(find . -name 'index.php' -not -path './includes/*' | sed 's|^\./||' | sort); do + case "$PAGES" in + *"|$page|"*) ;; + *) missing="$missing $page +" ;; + esac +done +if [ -n "$missing" ]; then + echo "generate-sitemap: pages are missing from the PAGES table in $0:" >&2 + printf '%s' "$missing" >&2 + exit 1 +fi + +emit() { + echo '' + # The sitemap protocol defines this exact namespace URI. It is an + # identifier, never fetched, and crawlers match it literally - an https + # variant is simply not a sitemap namespace. NOSONAR(S5332): the + # clear-text-protocol warning does not apply to an XML namespace. + echo '' # NOSONAR + # Redirected, not piped: a failure inside the loop must exit the script. + while IFS='|' read -r path source changefreq priority; do + [ -n "$path" ] || continue + + if [ ! -f "$source" ]; then + echo "generate-sitemap: $source does not exist" >&2 + exit 1 + fi + + lastmod=$(git log -1 --format=%cs -- "$source") + if [ -z "$lastmod" ]; then + lastmod=$(date -u +%F) + echo "generate-sitemap: $source has no commit yet, using today ($lastmod)" >&2 + fi + + echo ' ' + echo " ${BASE_URL}${path}" + echo " ${lastmod}" + echo " ${changefreq}" + echo " ${priority}" + echo ' ' + done <' +} + +if [ "$output" = '-' ]; then + emit +else + # mktemp rather than a $$-derived name: predictable temp paths are a + # symlink-attack vector. It creates the file 0600, so restore the mode a + # publicly served file needs before moving it into place. + tmp=$(mktemp "${output}.XXXXXX") + trap 'rm -f "$tmp"' EXIT + emit >"$tmp" + chmod 644 "$tmp" + mv "$tmp" "$output" + echo "generate-sitemap: wrote $output" +fi diff --git a/sitemap.xml b/sitemap.xml index 63d33fc..08124d0 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -2,72 +2,73 @@ https://openrtmp.org/ - 2026-08-09 + 2026-09-07 weekly 1.0 https://openrtmp.org/quickstart/ - 2026-07-18 + 2026-09-07 monthly 0.9 https://openrtmp.org/guides/ - 2026-08-09 + 2026-09-07 weekly 0.8 https://openrtmp.org/guides/self-hosted-rtmp-server-docker/ - 2026-08-09 + 2026-09-07 monthly 0.8 https://openrtmp.org/guides/rtmps-server-obs/ - 2026-07-18 + 2026-09-07 monthly 0.8 https://openrtmp.org/guides/rtmp-server-ha-clustering/ - 2026-08-09 + 2026-09-18 monthly 0.8 https://openrtmp.org/guides/enhanced-rtmp-hevc-av1-opus/ - 2026-07-18 + 2026-09-07 monthly 0.8 https://openrtmp.org/guides/openrtmp-noalbs-json-stats/ - 2026-07-26 + 2026-09-07 monthly 0.8 https://openrtmp.org/guides/openrtmp-vs-nginx-rtmp/ - 2026-08-09 + 2026-09-07 monthly 0.8 https://openrtmp.org/docs/ - 2026-08-09 + 2026-09-07 weekly 0.8 https://openrtmp.org/download/ - 2026-08-09 + 2026-09-07 weekly 0.8 https://openrtmp.org/legal/ + 2026-09-07 yearly 0.2