blob: 4a41e6e83f1226fd502e01348b2822e859c4a979 [file] [edit]
#!/usr/bin/env python3
# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import argparse
import json
import pathlib
import shutil
import subprocess
import sys
import tempfile
import venv
SITE_DIR = None
ONLY = None
ROOT = pathlib.Path(__file__).resolve().parents[3]
VERSIONS_JSON = ROOT / "docs" / "versions.json"
FONT_DIRS = ("_static/fonts", "_static/css/fonts")
LEGACY_FONTS = ("*.ttf", "*.eot", "*.svg", "*.woff")
DROP = ("blog", "_sources", "_images/social_previews", ".doctrees")
def run(cmd, cwd=None):
print("+ " + " ".join(str(c) for c in cmd))
subprocess.check_call(cmd, cwd=cwd)
def load_versions():
data = json.loads(VERSIONS_JSON.read_text(encoding="utf-8"))
entries = [e for e in data["versions"] if e.get("ref")]
return data["current"], entries
def banner(entry, current, root):
# Same component as docs/_templates/banner.html, loaded from the
# current site because these pages are built from an old tag.
name = entry["name"]
return (
'<meta name="robots" content="noindex">'
f'<link rel="stylesheet" href="{root}_static/css/banner.css">'
f'<script defer src="{root}_static/js/banner.js"></script>'
"<script>try{if(localStorage.getItem('psutil-banner')==="
f"'{name}')"
"{document.documentElement.classList.add("
"'site-banner-dismissed')}}catch(e){}</script>"
f'<div class="site-banner" role="note" data-banner-id="{name}">'
'<span>You are reading the documentation for psutil '
f'<b>{name}</b>. '
f'The current version is <a href="{root}">{current}</a>.</span>'
'<button class="site-banner-close" type="button" title="Dismiss" '
'aria-label="Dismiss this notice"></button>'
"</div>"
)
def inject(html_dir, entry, current):
count = 0
for path in sorted(html_dir.rglob("*.html")):
text = path.read_text(encoding="utf-8")
if "</head>" not in text:
sys.stderr.write(f"warning: no </head> in {path}\n")
continue
depth = len(path.relative_to(html_dir).parts)
root = "../" * depth
snippet = banner(entry, current, root)
path.write_text(
text.replace("</head>", snippet + "</head>", 1), encoding="utf-8"
)
count += 1
return count
def trim(html_dir):
for rel in DROP:
target = html_dir / rel
if target.is_dir():
shutil.rmtree(target)
for rel in FONT_DIRS:
font_dir = html_dir / rel
if not font_dir.is_dir():
continue
for pattern in LEGACY_FONTS:
for path in font_dir.rglob(pattern):
path.unlink()
def build_one(entry, current, site_dir):
tmp = pathlib.Path(tempfile.mkdtemp(prefix="psutil-docs-"))
worktree = tmp / "src"
try:
run(
["git", "worktree", "add", "--detach", worktree, entry["ref"]],
cwd=ROOT,
)
env = tmp / "venv"
venv.create(env, with_pip=True)
python = env / "bin" / "python"
run([
python,
"-m",
"pip",
"install",
"--quiet",
"-r",
worktree / "docs" / "requirements.txt",
])
run(["make", "html", f"PYTHON={python}"], cwd=worktree / "docs")
html_dir = worktree / "docs" / "_build" / "html"
pages = inject(html_dir, entry, current)
trim(html_dir)
dst = site_dir / entry["url"].strip("/")
if dst.exists():
shutil.rmtree(dst)
shutil.copytree(html_dir, dst)
size = sum(p.stat().st_size for p in dst.rglob("*") if p.is_file())
print(f" {entry['name']}: {pages} pages, {size / 1048576:.1f} MB")
finally:
subprocess.call(
["git", "worktree", "remove", "--force", worktree], cwd=ROOT
)
shutil.rmtree(tmp, ignore_errors=True)
def parse_cli():
global SITE_DIR, ONLY
parser = argparse.ArgumentParser(
description="Build past doc releases into a built site."
)
parser.add_argument("site", help="built HTML dir, e.g. docs/_build/html")
parser.add_argument(
"--only", default=None, help="build just this version name"
)
args = parser.parse_args()
SITE_DIR = pathlib.Path(args.site).resolve()
ONLY = args.only
def main():
parse_cli()
if not SITE_DIR.is_dir():
sys.exit(f"error: {SITE_DIR} does not exist; run `make html` first")
current, entries = load_versions()
if ONLY:
entries = [e for e in entries if e["name"] == ONLY]
if not entries:
sys.exit(f"error: no version named {ONLY!r} with a ref")
if not entries:
print("no past versions to build")
return
for entry in entries:
print(f"building {entry['name']} from {entry['ref']}")
build_one(entry, current, SITE_DIR)
if __name__ == "__main__":
main()