summaryrefslogtreecommitdiff
path: root/scripts/generate-sitemap.py
blob: 4b7ff030769acedc1a4195ae5e001f1b3347a42b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#!/usr/bin/env python3
import os
import sys
import datetime
import xml.etree.ElementTree as ET
from xml.dom import minidom

BASE_URL = "https://vyos.net"

ASSET_DIRS = {"img", "js", "favicon"}

OVERRIDES = {
    "/get/": {"changefreq": "daily", "priority": "0.8"},
    "/get/nightly-builds/": {"changefreq": "daily", "priority": "0.5"},
}

DEPTH_DEFAULTS = [
    {"changefreq": "daily",   "priority": "1.0"},   # depth 0
    {"changefreq": "weekly",  "priority": "0.8"},   # depth 1
    {"changefreq": "monthly", "priority": "0.5"},   # depth 2+
]


def find_pages(site_dir):
    """Return list of absolute paths to routable page source files."""
    pages = []
    for dirpath, dirnames, filenames in os.walk(site_dir):
        dirnames[:] = [d for d in dirnames if d not in ASSET_DIRS]
        for fname in filenames:
            if fname.endswith((".md", ".html")):
                pages.append(os.path.join(dirpath, fname))
    return pages


def derive_url(filepath, site_dir):
    """Derive canonical URL from a source file path relative to site_dir."""
    rel = os.path.relpath(filepath, site_dir)
    parts = rel.replace(os.sep, "/").split("/")
    stem = os.path.splitext(parts[-1])[0]
    if stem == "index":
        path_parts = parts[:-1]
    else:
        path_parts = parts[:-1] + [stem]
    if not path_parts:
        url_path = "/"
    else:
        url_path = "/" + "/".join(path_parts) + "/"
    return BASE_URL + url_path


def get_heuristics(url_path):
    """Return (changefreq, priority) tuple for a URL path string."""
    if url_path in OVERRIDES:
        o = OVERRIDES[url_path]
        return o["changefreq"], o["priority"]
    depth = len([s for s in url_path.split("/") if s])
    idx = min(depth, 2)
    d = DEPTH_DEFAULTS[idx]
    return d["changefreq"], d["priority"]


def generate_sitemap(site_dir, build_dir, today=None):
    """Discover pages in site_dir and write build_dir/sitemap.xml."""
    if today is None:
        today = datetime.date.today().isoformat()

    if not os.path.isdir(site_dir):
        print(f"Error: site directory not found: {site_dir}", file=sys.stderr)
        sys.exit(1)

    pages = find_pages(site_dir)
    if not pages:
        print("Error: no routable pages discovered in site/", file=sys.stderr)
        sys.exit(1)

    # Derive URLs, detect duplicates
    url_to_source = {}
    for page in pages:
        url = derive_url(page, site_dir)
        if url in url_to_source:
            print(
                f"Error: duplicate URL {url} from:\n"
                f"  {url_to_source[url]}\n"
                f"  {page}",
                file=sys.stderr,
            )
            sys.exit(1)
        url_to_source[url] = page

    # Build XML tree
    urlset = ET.Element("urlset")
    urlset.set("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9")

    for url in sorted(url_to_source):
        url_path = url[len(BASE_URL):]
        changefreq, priority = get_heuristics(url_path)
        url_el = ET.SubElement(urlset, "url")
        ET.SubElement(url_el, "loc").text = url
        ET.SubElement(url_el, "lastmod").text = today
        ET.SubElement(url_el, "changefreq").text = changefreq
        ET.SubElement(url_el, "priority").text = priority

    # Pretty-print via minidom
    raw = ET.tostring(urlset, encoding="unicode")
    dom = minidom.parseString(raw)
    pretty = dom.toprettyxml(indent="  ", encoding="UTF-8")

    out_path = os.path.join(build_dir, "sitemap.xml")
    os.makedirs(build_dir, exist_ok=True)
    with open(out_path, "wb") as f:
        f.write(pretty)
    print(f"Wrote {out_path}")


def main():
    script_dir = os.path.dirname(os.path.abspath(__file__))
    repo_root = os.path.dirname(script_dir)
    site_dir = os.path.join(repo_root, "site")
    build_dir = os.path.join(repo_root, "build")
    generate_sitemap(site_dir, build_dir)


if __name__ == "__main__":
    main()