-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathredirects.py
More file actions
executable file
·80 lines (63 loc) · 2.65 KB
/
redirects.py
File metadata and controls
executable file
·80 lines (63 loc) · 2.65 KB
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
#!/usr/bin/env python3
"""
Post-build script that generates apache redirect rules by following
each page's history in the git log.
"""
import subprocess
from pathlib import Path
DOCS_DIR = Path("docs")
SITE_DIR = Path("site")
# Redirect window
SINCE = "12 months ago"
def run(cmd: list[str]) -> list[str]:
"""Run a command with arguments and return its output."""
return subprocess.check_output(cmd, text=True).splitlines()
def current_pages() -> list[Path]:
"""Get relative paths to all current Markdown pages."""
return [Path(p) for p in run(["git", "ls-files", "docs"]) if p.endswith(".md")]
def previous_paths(path: Path) -> list[Path]:
"""Get all previous paths to the given Markdown page."""
output = run([
"git", "log", "--first-parent", "main", f"--since={SINCE}",
"--follow", "--name-status", "--format=",
"--", str(path)
])
# Get the second word (old path) of each line with rename status
return [Path(line.split()[1]) for line in output if line.startswith("R")]
def path_to_url(path: Path) -> str:
"""Convert a source file path to a normalized url."""
# Remove docs root if present
if path.is_relative_to(DOCS_DIR):
path = path.relative_to(DOCS_DIR)
# Remove index file if present
if path.name in ("start.txt", "index.md"):
return str(path.parent)
# Remove extension (txt or md)
return str(path.with_suffix(""))
def main() -> None:
print("Running redirects.py")
rules: set[tuple[str, str]] = set()
# Discover the redirect rules
for new_path in current_pages():
for old_path in previous_paths(new_path):
if "files" not in old_path.parts:
# Convert the source file paths to urls
old_url = path_to_url(old_path)
new_url = path_to_url(new_path)
# Emit rule only if the path has changed
if old_url != new_url:
rules.add((old_url, new_url))
# Append the server config file
with open(SITE_DIR / ".htaccess", "a") as file:
file.write("\n# Legacy urls generated by redirects.py\n")
for old_url, new_url in sorted(rules, key=lambda r: r[1]):
# Avoid shadowing an existing page if the old link is valid in the build
if (SITE_DIR / old_url / "index.html").exists():
print(old_url, "exists, skipping redirect")
continue
# The new URL should end with a slash, because it represents a directory
line = f"RedirectMatch 301 ^/{old_url}/*$ /{new_url}/"
print("+", line)
file.write(line + "\n")
if __name__ == "__main__":
main()