mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-07-26 12:08:15 +00:00
fix: derive sitemap dates from page history
This commit is contained in:
@@ -37,6 +37,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
|
|||||||
@@ -83,8 +83,10 @@ for (const metadata of requiredSupportSocialMetadata) {
|
|||||||
throw new Error(`site-access-help.html is missing SEO metadata: ${metadata}`);
|
throw new Error(`site-access-help.html is missing SEO metadata: ${metadata}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (/<lastmod>/.test(websiteBuild) || /const today = new Date\(\)/.test(websiteBuild)) {
|
if (!websiteBuild.includes("['log', '-1', '--format=%cs', '--', ...sourceFiles]")
|
||||||
throw new Error('Sitemap must not claim synthetic modification dates for every URL');
|
|| !websiteBuild.includes("lastmod(['website/site-access-help.html'])")
|
||||||
|
|| /const today = new Date\(\)/.test(websiteBuild)) {
|
||||||
|
throw new Error('Sitemap lastmod values must come from the mapped source files in Git');
|
||||||
}
|
}
|
||||||
const documentedRelayUrls = [...llmsText.matchAll(/`(wss:\/\/[^`\s]+)`/g)].map(([, value]) => new URL(value));
|
const documentedRelayUrls = [...llmsText.matchAll(/`(wss:\/\/[^`\s]+)`/g)].map(([, value]) => new URL(value));
|
||||||
const hasCanonicalPublicRelay = documentedRelayUrls.some((url) => (
|
const hasCanonicalPublicRelay = documentedRelayUrls.some((url) => (
|
||||||
@@ -199,7 +201,7 @@ if (!/animation:\s*none\s*!important/.test(reducedMotionRule)) {
|
|||||||
console.log('Extension mockup theme-sensitive text uses theme-aware colors');
|
console.log('Extension mockup theme-sensitive text uses theme-aware colors');
|
||||||
console.log('Landing CSS is render-blocking, single-request, and cascade-stable');
|
console.log('Landing CSS is render-blocking, single-request, and cascade-stable');
|
||||||
console.log('Landing head discovers a complete and architecture-accurate llms.txt');
|
console.log('Landing head discovers a complete and architecture-accurate llms.txt');
|
||||||
console.log('Support-page social metadata and sitemap dates remain truthful');
|
console.log('Support-page social metadata and Git-derived sitemap dates remain truthful');
|
||||||
console.log('Website self-hosting examples include production secrets and consistent networking');
|
console.log('Website self-hosting examples include production secrets and consistent networking');
|
||||||
console.log('Foreground film birds use complete, always-on wing and glide animations');
|
console.log('Foreground film birds use complete, always-on wing and glide animations');
|
||||||
console.log('All Getting Started mockups define explicit light-theme surfaces');
|
console.log('All Getting Started mockups define explicit light-theme surfaces');
|
||||||
|
|||||||
+50
-20
@@ -5,6 +5,7 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
|
const { execFileSync } = require('child_process');
|
||||||
const sharp = require('sharp');
|
const sharp = require('sharp');
|
||||||
const esbuild = require('esbuild');
|
const esbuild = require('esbuild');
|
||||||
const htmlnano = require('htmlnano');
|
const htmlnano = require('htmlnano');
|
||||||
@@ -549,7 +550,7 @@ async function compile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── 5.5 Generate dynamic sitemap ──
|
// ── 5.5 Generate dynamic sitemap ──
|
||||||
generateSitemap(wwwDir);
|
generateSitemap(websiteDir, wwwDir);
|
||||||
|
|
||||||
// Auto-copy Google verification files and IndexNow/txt key files from website source to www root
|
// Auto-copy Google verification files and IndexNow/txt key files from website source to www root
|
||||||
const websiteFiles = fs.readdirSync(websiteDir);
|
const websiteFiles = fs.readdirSync(websiteDir);
|
||||||
@@ -697,7 +698,7 @@ async function compile() {
|
|||||||
console.log('KoalaSync build finished successfully! Output: website/www/');
|
console.log('KoalaSync build finished successfully! Output: website/www/');
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateSitemap(wwwDir) {
|
function generateSitemap(websiteDir, wwwDir) {
|
||||||
const languages = [
|
const languages = [
|
||||||
{ code: 'en', prefix: '', hreflang: 'en' },
|
{ code: 'en', prefix: '', hreflang: 'en' },
|
||||||
{ code: 'de', prefix: 'de/', hreflang: 'de' },
|
{ code: 'de', prefix: 'de/', hreflang: 'de' },
|
||||||
@@ -716,6 +717,33 @@ function generateSitemap(wwwDir) {
|
|||||||
{ code: 'pt', prefix: 'pt/', hreflang: 'pt' }
|
{ code: 'pt', prefix: 'pt/', hreflang: 'pt' }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const repoRoot = path.resolve(websiteDir, '..');
|
||||||
|
const lastModifiedCache = new Map();
|
||||||
|
|
||||||
|
function getLastModified(sourceFiles) {
|
||||||
|
const cacheKey = sourceFiles.slice().sort().join('\0');
|
||||||
|
if (lastModifiedCache.has(cacheKey)) return lastModifiedCache.get(cacheKey);
|
||||||
|
let date = null;
|
||||||
|
try {
|
||||||
|
const output = execFileSync(
|
||||||
|
'git',
|
||||||
|
['log', '-1', '--format=%cs', '--', ...sourceFiles],
|
||||||
|
{ cwd: repoRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }
|
||||||
|
).trim();
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}$/.test(output)) date = output;
|
||||||
|
} catch (_) {
|
||||||
|
// Source archives and minimal deployment environments may not contain
|
||||||
|
// Git metadata. Omitting lastmod is more accurate than inventing a date.
|
||||||
|
}
|
||||||
|
lastModifiedCache.set(cacheKey, date);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
function lastmod(sourceFiles, indent = ' ') {
|
||||||
|
const date = getLastModified(sourceFiles);
|
||||||
|
return date ? `\n${indent}<lastmod>${date}</lastmod>` : '';
|
||||||
|
}
|
||||||
|
|
||||||
let xml = `<?xml version="1.0" encoding="UTF-8"?>
|
let xml = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||||
xmlns:xhtml="http://www.w3.org/1999/xhtml">`;
|
xmlns:xhtml="http://www.w3.org/1999/xhtml">`;
|
||||||
@@ -723,37 +751,38 @@ function generateSitemap(wwwDir) {
|
|||||||
// Static legal pages
|
// Static legal pages
|
||||||
xml += `
|
xml += `
|
||||||
<url>
|
<url>
|
||||||
<loc>https://sync.koalastuff.net/imprint</loc>
|
<loc>https://sync.koalastuff.net/imprint</loc>${lastmod(['website/imprint.html'])}
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.3</priority>
|
<priority>0.3</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://sync.koalastuff.net/privacy</loc>
|
<loc>https://sync.koalastuff.net/privacy</loc>${lastmod(['website/privacy.html'])}
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.3</priority>
|
<priority>0.3</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://sync.koalastuff.net/site-access-help.html</loc>
|
<loc>https://sync.koalastuff.net/site-access-help.html</loc>${lastmod(['website/site-access-help.html'])}
|
||||||
<changefreq>weekly</changefreq>
|
<changefreq>weekly</changefreq>
|
||||||
<priority>0.8</priority>
|
<priority>0.8</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://sync.koalastuff.net/de/impressum</loc>
|
<loc>https://sync.koalastuff.net/de/impressum</loc>${lastmod(['website/impressum-de.html'])}
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.3</priority>
|
<priority>0.3</priority>
|
||||||
</url>
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://sync.koalastuff.net/de/datenschutz</loc>
|
<loc>https://sync.koalastuff.net/de/datenschutz</loc>${lastmod(['website/datenschutz-de.html'])}
|
||||||
<changefreq>monthly</changefreq>
|
<changefreq>monthly</changefreq>
|
||||||
<priority>0.3</priority>
|
<priority>0.3</priority>
|
||||||
</url>`;
|
</url>`;
|
||||||
|
|
||||||
function addPage(relativePath, changefreq, priority) {
|
function addPage(relativePath, changefreq, priority, templateSource) {
|
||||||
for (const lang of languages) {
|
for (const lang of languages) {
|
||||||
const loc = `https://sync.koalastuff.net/${lang.prefix}${relativePath}`;
|
const loc = `https://sync.koalastuff.net/${lang.prefix}${relativePath}`;
|
||||||
|
const sourceFiles = [templateSource, `website/locales/${lang.code}.json`];
|
||||||
xml += `
|
xml += `
|
||||||
<url>
|
<url>
|
||||||
<loc>${loc}</loc>
|
<loc>${loc}</loc>${lastmod(sourceFiles)}
|
||||||
<changefreq>${changefreq}</changefreq>
|
<changefreq>${changefreq}</changefreq>
|
||||||
<priority>${priority}</priority>`;
|
<priority>${priority}</priority>`;
|
||||||
for (const alt of languages) {
|
for (const alt of languages) {
|
||||||
@@ -768,24 +797,25 @@ function generateSitemap(wwwDir) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addPage('', 'weekly', '1.0');
|
addPage('', 'weekly', '1.0', 'website/template.html');
|
||||||
addPage('alternatives', 'weekly', '0.7');
|
addPage('alternatives', 'weekly', '0.7', 'website/alternatives/index.html');
|
||||||
|
|
||||||
const subpages = [
|
const subpages = [
|
||||||
'alternatives/teleparty',
|
['alternatives/teleparty', 'website/alternatives/teleparty.html'],
|
||||||
'alternatives/screen-sharing',
|
['alternatives/screen-sharing', 'website/alternatives/screen-sharing.html'],
|
||||||
'alternatives/watch2gether',
|
['alternatives/watch2gether', 'website/alternatives/watch2gether.html'],
|
||||||
'alternatives/scener',
|
['alternatives/scener', 'website/alternatives/scener.html'],
|
||||||
'alternatives/kosmi',
|
['alternatives/kosmi', 'website/alternatives/kosmi.html'],
|
||||||
'alternatives/twoseven'
|
['alternatives/twoseven', 'website/alternatives/twoseven.html']
|
||||||
];
|
];
|
||||||
for (const sub of subpages) {
|
for (const [sub, templateSource] of subpages) {
|
||||||
addPage(sub, 'weekly', '0.7');
|
addPage(sub, 'weekly', '0.7', templateSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
xml += `\n</urlset>\n`;
|
xml += `\n</urlset>\n`;
|
||||||
|
|
||||||
fs.writeFileSync(path.join(wwwDir, 'sitemap.xml'), xml.trim() + '\n', 'utf8');
|
fs.writeFileSync(path.join(wwwDir, 'sitemap.xml'), xml.trim() + '\n', 'utf8');
|
||||||
console.log(' ✓ Dynamically generated sitemap.xml without synthetic modification dates');
|
const datedEntries = (xml.match(/<lastmod>/g) || []).length;
|
||||||
|
console.log(` ✓ Dynamically generated sitemap.xml with ${datedEntries} Git-derived modification dates`);
|
||||||
}
|
}
|
||||||
compile().catch(err => { console.error('Build failed:', err); process.exit(1); });
|
compile().catch(err => { console.error('Build failed:', err); process.exit(1); });
|
||||||
|
|||||||
Reference in New Issue
Block a user