summaryrefslogtreecommitdiffstats
path: root/sphinx/ext/githubpages.py
blob: c9be928e8b11cc0e21834227e9e5457a42fb4c9f (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
"""To publish HTML docs at GitHub Pages, create .nojekyll file."""

from __future__ import annotations

import contextlib
import os
import urllib.parse
from typing import TYPE_CHECKING, Any

import sphinx

if TYPE_CHECKING:
    from sphinx.application import Sphinx
    from sphinx.environment import BuildEnvironment


def _get_domain_from_url(url: str) -> str:
    """Get the domain from a URL."""
    return url and urllib.parse.urlparse(url).hostname or ''


def create_nojekyll_and_cname(app: Sphinx, env: BuildEnvironment) -> None:
    """Manage the ``.nojekyll`` and ``CNAME`` files for GitHub Pages.

    For HTML-format builders (e.g. 'html', 'dirhtml') we unconditionally create
    the ``.nojekyll`` file to signal that GitHub Pages should not run Jekyll
    processing.

    If the :confval:`html_baseurl` option is set, we also create a CNAME file
    with the domain from ``html_baseurl``, so long as it is not a ``github.io``
    domain.

    If this extension is loaded and the domain in ``html_baseurl`` no longer
    requires a CNAME file, we remove any existing ``CNAME`` files from the
    output directory.
    """
    if app.builder.format != 'html':
        return

    app.builder.outdir.joinpath('.nojekyll').touch()
    cname_path = os.path.join(app.builder.outdir, 'CNAME')

    domain = _get_domain_from_url(app.config.html_baseurl)
    # Filter out GitHub Pages domains, as they do not require CNAME files.
    if domain and not domain.endswith(".github.io"):
        with open(cname_path, 'w', encoding="utf-8") as f:
            # NOTE: don't write a trailing newline. The `CNAME` file that's
            # auto-generated by the GitHub UI doesn't have one.
            f.write(domain)
    else:
        with contextlib.suppress(FileNotFoundError):
            os.unlink(cname_path)


def setup(app: Sphinx) -> dict[str, Any]:
    app.connect('env-updated', create_nojekyll_and_cname)
    return {'version': sphinx.__display_version__, 'parallel_read_safe': True}