awesome-prometheus-alerts/site/astro.config.mjs
Samuel Berthe 79afa21610
feat/astro migration (#538)
* feat: migrate website from Jekyll to Astro

Rebuilds the site using Astro (SSG) with Tailwind CSS v4, replacing the
Jekyll/Cayman theme. Key changes:

- Splits the monolithic /rules page into 110 statically-generated pages
  (92 per-service + 13 group index + homepage + guide pages) for SEO
- URL structure: /rules/[group-slug]/[service-slug]/ with backward-
  compatibility redirect map for old anchor-based URLs (/rules#redis)
- Modern UI: Prometheus-orange accent, dark mode (system + toggle),
  sticky sidebar, responsive layout, copy-to-clipboard per rule/section
- SEO: per-page <title>, <meta description>, Open Graph, Twitter Card,
  canonical URLs, sitemap.xml via @astrojs/sitemap
- GEO: FAQPage JSON-LD schema on each service page (rules as Q&A pairs
  for AI search engines), TechArticle schema, BreadcrumbList
- Search: Pagefind (build-time index, lazy-loaded, ~200KB)
- Zero JS by default; copy buttons and theme toggle use inline scripts
- New CI: .github/workflows/deploy.yml builds Astro + Pagefind and
  deploys to GitHub Pages via actions/deploy-pages
- Existing dist.yml and test.yml workflows are untouched
- _data/rules.yml remains the single source of truth

Note: GitHub Pages source must be changed from "Build from branch"
(Jekyll) to "GitHub Actions" in repository settings.

* doc: new website based on astro

* refactor: remove previous website

* chore: add npm dependabot for Astro site + scope CI to _data changes

* Update site/astro.config.mjs

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update site/src/components/CopyButton.astro

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* oops

* fix: strip trailing slash from BASE_URL to prevent double slashes in URLs

Agent-Logs-Url: https://github.com/samber/awesome-prometheus-alerts/sessions/c85937ba-1855-4b8a-a72b-847eab1c8639

Co-authored-by: samber <2951285+samber@users.noreply.github.com>

* fix: resolve Astro build errors in astro.config.mjs

- Remove assetsInclude yml which caused Vite to treat YAML files as static assets instead of running them through the custom YAML transform plugin; data.groups was undefined at runtime because the import resolved to a URL rather than parsed content
- Deduplicate old-path redirects: emit only the slash-less variant per service to avoid Astro router collision warnings (trailing-slash variant is handled automatically)

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: samber <2951285+samber@users.noreply.github.com>
2026-04-10 21:08:06 +02:00

105 lines
3.8 KiB
JavaScript

import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
import sitemap from '@astrojs/sitemap';
import icon from 'astro-icon';
import { parse as parseYaml } from 'yaml';
import { readFileSync } from 'fs';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
function normalizeViteId(id) {
const cleanId = id.split('?', 1)[0].split('#', 1)[0];
return cleanId.startsWith('/@fs/') ? cleanId.slice(4) : cleanId;
}
/** Custom Vite plugin that parses YAML files using the 'yaml' package,
* which tolerates duplicate keys (last one wins) unlike js-yaml 4.x. */
function yamlPlugin() {
return {
name: 'vite-plugin-yaml-tolerant',
transform(code, id) {
const normalizedId = normalizeViteId(id);
if (!normalizedId.endsWith('.yml') && !normalizedId.endsWith('.yaml')) return null;
const content = typeof code === 'string' ? code : readFileSync(resolve(normalizedId), 'utf-8');
const data = parseYaml(content, { merge: true, strict: false, uniqueKeys: false });
return {
code: `export default ${JSON.stringify(data)};`,
map: null,
};
},
};
}
const toSlug = (name) =>
name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
/** Build redirect map: old flat /rules/{service} paths → new /rules/{group}/{service}/ paths */
function buildRedirects(base) {
try {
const rulesPath = resolve(__dirname, '../_data/rules.yml');
const raw = readFileSync(rulesPath, 'utf-8');
const { groups } = parseYaml(raw, { merge: true, strict: false, uniqueKeys: false });
const redirects = {};
for (const group of groups) {
const groupSlug = toSlug(group.name);
for (const service of group.services) {
const serviceSlug = toSlug(service.name);
// Old anchor slug (spaces → hyphens only, no other substitutions)
const oldSlug = service.name.replace(/ /g, '-').toLowerCase();
const newPath = `${base}/rules/${groupSlug}/${serviceSlug}/`;
// Redirect from flat old path (without trailing slash; Astro handles the slash variant)
const oldPath = `${base}/rules/${oldSlug}`;
if (oldPath !== newPath && oldPath !== newPath.slice(0, -1)) {
redirects[oldPath] = { destination: newPath, status: 301 };
}
}
}
return redirects;
} catch {
return {};
}
}
const base = '/awesome-prometheus-alerts';
export default defineConfig({
site: 'https://samber.github.io',
base,
redirects: buildRedirects(base),
output: 'static',
integrations: [
tailwind({ applyBaseStyles: false }),
sitemap({
serialize(item) {
const path = new URL(item.url).pathname;
const segments = path.replace(/^\/|\/$/g, '').split('/').filter(Boolean);
// segments[0] = 'awesome-prometheus-alerts', [1] = 'rules'|guide, [2] = group, [3] = service
if (segments.length <= 1) {
// Homepage
return { ...item, changefreq: 'weekly', priority: 1.0, lastmod: new Date() };
}
if (segments.length === 2 && segments[1] === 'rules') {
// /rules/ index
return { ...item, changefreq: 'weekly', priority: 0.9, lastmod: new Date() };
}
if (segments.length === 3 && segments[1] === 'rules') {
// /rules/[group]/ index
return { ...item, changefreq: 'monthly', priority: 0.7, lastmod: new Date() };
}
if (segments.length === 4 && segments[1] === 'rules') {
// /rules/[group]/[service]/ — main content pages
return { ...item, changefreq: 'monthly', priority: 0.8, lastmod: new Date() };
}
// Guide pages and others
return { ...item, changefreq: 'yearly', priority: 0.6, lastmod: new Date() };
},
}),
icon(),
],
vite: {
plugins: [yamlPlugin()],
},
});