Skip to content

Commit

Permalink
Render Sphinx /docs in docusaurus (#2077)
Browse files Browse the repository at this point in the history
* Render Sphinx to markdown

* Adjust CFEP index format

* Adjust some lists and headers

* Fix broken link? (works locally)

* Only throw errors on GHA CI (warn on local and Netlify)

* push a couple broken links to demo link checker

* add an additional link checker GHA step

* Use lychee

* configure lychee

* less verbosity

* remap instead

* try with this one

* where's the report?

* remove verbosity

* exclude comma separated URLs

* a different broken anchor

* raise if document not in sidebar

* do not show editUrl on docs/ for now (broken)

* remove intentionally broken links; this should pass

* upgrade prism highlighting

* fix indentation in some lists

* Publish _static assets

* pathname:// for linked files

* Use :ref: and :doc: for internal links instead of https://conda-forge.org/ ones

* Adjust FAQ output

* Formatting adjustments

* Fix Maintainer FAQ too

* add basic local search for now

* add goatcounter

* Use Algolia search

* exclude algolia from link checker

* more explicit

* Upgrade to docusaurus 3.1.1 and enable onBrokenAnchors

* Fix broken generation of anchor

* Remove todo list from misc

* vendor goatcounter ourselves

* only deploy stats on GHA site

* BUG attempt to fix rendering of core and emeritus

* BUG maybe one more level?

* Fix Jinja rendering in governance and cfep-index

* get cfep list from repo archive, not api

---------

Co-authored-by: Matthew R. Becker <beckermr@users.noreply.github.com>
  • Loading branch information
jaimergp and beckermr authored Feb 16, 2024
1 parent d6d4d60 commit 5040828
Show file tree
Hide file tree
Showing 37 changed files with 1,378 additions and 857 deletions.
2 changes: 2 additions & 0 deletions .ci_scripts/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ dependencies:
- python-rapidjson
- termcolor
- nodejs 20.*
- pip:
- https://github.com/jaimergp/sphinx-markdown-builder/archive/admonitions.tar.gz
51 changes: 47 additions & 4 deletions .ci_scripts/generate_cfep_index.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import re
import requests
import sys
import tarfile
from dataclasses import dataclass
from pathlib import Path
from tempfile import TemporaryDirectory

REPO_URL = "https://github.com/conda-forge/cfep"
REPO_ARCHIVE = "https://github.com/conda-forge/cfep/archive/main.tar.gz"
REPO_CONTENTS = "https://api.github.com/repos/conda-forge/cfep/contents/"
TITLE_PATTERN = "<td>\s*Title\s*</td><td>\s*(.*)\s*</td>"
STATUS_PATTERN = "<td>\s*Status\s*</td><td>\s*(.*)\s*</td>"
Expand Down Expand Up @@ -31,7 +36,7 @@ def md_link(self) -> str:
)


def get_cfeps():
def get_cfeps_from_gh_api():
"""Generator that returns all CFEPs from GitHub repo"""
response = requests.get(
REPO_CONTENTS, headers={"Accept": "application/vnd.github.v3+json"}
Expand All @@ -56,10 +61,48 @@ def get_cfeps():
yield Cfep(content["name"], title, status, content["html_url"])


def get_cfeps():
"""Return a generator of CFEPs, by traversing the contents of the repo archive"""
r = requests.get(REPO_ARCHIVE, stream=True)
r.raise_for_status()
with TemporaryDirectory() as tmp:
# Write the tarball to a temporary directory
tarball = Path(tmp) / "cfep.tar.gz"
with tarball.open("wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
# Extract the tarball
extracted_dir = Path(tmp) / "cfep"
extracted_dir.mkdir()
with tarfile.open(tarball) as tar:
tar.extractall(extracted_dir)
# Traverse the extracted directory and return all CFEPs
for cfep in sorted(extracted_dir.rglob("cfep-*.md")):
name = cfep.name
url = f"{REPO_URL}/blob/main/{name}"
if name == "cfep-00.md":
# Hardcode title and status for CFEP-00
yield Cfep(name, "CFEP Template", "Proposed", url)
continue
cfep_text = cfep.read_text()
m = re.search(TITLE_PATTERN, cfep_text)
title = m.group(1).strip() if m else ""
m = re.search(STATUS_PATTERN, cfep_text)
status = m.group(1).strip() if m else ""
yield Cfep(name, title, status, url)


def write_cfep_index():
with CFEP_INDEX_RST.open("a") as f:
for cfep in get_cfeps():
f.write(f"* {cfep.rst_link()}\n")
contents = CFEP_INDEX_RST.read_text()
if ".. REPLACE-THIS-LINE-WITH-THE-INDEX-OF-CFEPs" not in contents:
print("!!! Skipping writing CFEP index. Already rendered?", file=sys.stderr)
return
rst_links = [f"- {cfep.rst_link()}" for cfep in get_cfeps()]
contents = contents.replace(
".. REPLACE-THIS-LINE-WITH-THE-INDEX-OF-CFEPs",
"\n".join(rst_links)
)
CFEP_INDEX_RST.write_text(contents)


if __name__ == "__main__":
Expand Down
108 changes: 108 additions & 0 deletions .ci_scripts/sphinx_markdown_to_docusaurus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""
Given the Markdown output of a sphinx site, place it in the correct place for Docusaurus.
"""
import re
import sys
from pathlib import Path

# The ordering of the sidebar is defined here. This is a list of paths to the markdown files
# as they would appear after being moved into /docs. In other words, how Docusaurus would see them.
SIDEBAR_ORDERING = [
"/docs/index.md",
"/docs/user/index.md",
"/docs/user/introduction.md",
"/docs/user/tipsandtricks.md",
"/docs/user/ci-skeleton.md",
"/docs/user/faq.md",
"/docs/user/contributing.md",
"/docs/user/how_to_get_help.md",
"/docs/user/talks.md",
"/docs/maintainer/index.md",
"/docs/maintainer/infrastructure.md",
"/docs/maintainer/adding_pkgs.md",
"/docs/maintainer/updating_pkgs.md",
"/docs/maintainer/pinning_deps.md",
"/docs/maintainer/conda_forge_yml.md",
"/docs/maintainer/knowledge_base.md",
"/docs/maintainer/maintainer_faq.md",
"/docs/orga/index.md",
"/docs/orga/guidelines.md",
"/docs/orga/governance.md",
"/docs/orga/subteams.md",
"/docs/orga/joining-the-team.md",
"/docs/orga/funding",
"/docs/orga/minutes/",
"/docs/orga/cfep-index.md",
"/docs/orga/getting-in-touch.md",
"/docs/orga/funding/index.md",
"/docs/misc/index.md",
"/docs/contracting/index.md",
]
# Note we also ignore the /minutes/.* files later in the code
SIDEBAR_ORDERING_IGNORED = {
"/docs/orga/funding/gsod-2023.md",
"/docs/orga/funding/gsoc-2023.md",
"/docs/orga/funding/sdg-2023-1.md",
}


def get_mds(basedir):
for path in Path(basedir).glob("**/*.md"):
yield path


def sphinx_md_to_docusaurus_md(basedir, mdpath, targetdir, ordering=None):
relmd = mdpath.relative_to(basedir)
target_path = Path(targetdir, relmd)
target_path.parent.mkdir(parents=True, exist_ok=True)
text = mdpath.read_text()
text = text.replace("00_intro.md", "index.md")
text = text.replace("(/_static/", "(pathname:///_static/")
text = re.sub(r"\]\((/\S+\.\S+)\)", r"](pathname://\1)", text)
title = next(re.finditer(r"^# (.+)$", text, re.MULTILINE), None)
if not text.lstrip().startswith("---"):
frontmatter = []
if title:
frontmatter.append(f"title: '{title.group(1)}'")
if ordering is not None:
frontmatter.append(f"sidebar_position: {ordering}")
if frontmatter:
text = "---\n" + "\n".join(frontmatter) + "\n---\n\n" + text
if mdpath.name == "00_intro.md":
target_path = target_path.parent / "index.md"
target_path.write_text(text)


def main(build_dir, targetdir):
md_dir = Path(build_dir)
not_in_sidebar = []
for path in md_dir.glob("**/*.md"):
print("Processing MD", path)
try:
relmd = path.relative_to(md_dir).as_posix()
relmd_key = f"/docs/{relmd}".replace("00_intro.md", "index.md")
ordering = SIDEBAR_ORDERING.index(relmd_key)
except ValueError:
if "/minutes/" in relmd_key or relmd_key in SIDEBAR_ORDERING_IGNORED:
ordering = 1000
else:
not_in_sidebar.append(path)
continue
sphinx_md_to_docusaurus_md(md_dir, path, targetdir, ordering=ordering)
if not_in_sidebar:
print(
"The following files are not in the sidebar:",
*not_in_sidebar,
sep="\n- ",
file=sys.stderr,
)
print(
"Edit SIDEBAR_ORDERING in .ci_scripts/sphinx_markdown_to_docusaurus.py",
file=sys.stderr,
)
sys.exit(1)


if __name__ == "__main__":
build_dir, targetdir = sys.argv[1:3]
main(build_dir, targetdir)
30 changes: 11 additions & 19 deletions .ci_scripts/update_docs
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,27 @@

set -ex

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BASE_DIR=$(dirname $HERE)

# configure bot account
if [[ "$CI" == "1" ]]; then
git config --global user.email "pelson.pub+conda-forge@gmail.com"
git config --global user.name "conda-forge-admin"
git checkout -b new_site_content
fi

python .ci_scripts/generate_cfep_index.py

pushd sphinx/src

pushd "$BASE_DIR/sphinx/src"
make clean
rm -rf "$BASE_DIR/static-sphinx"
# -W --keep-going: list all warnings but fail build in case there are any
# -n: check validity of all links
make html SPHINXOPTS="-W --keep-going -n"
linkcheck_failed=0
make linkcheck > /dev/null || linkcheck_failed=1
python ../../.ci_scripts/display_linkcheck.py _build/linkcheck/output.json

if [[ "${GHREF}" != "refs/heads/main" ]]; then
test "$linkcheck_failed" -eq 0
fi

# Move rendered Sphinx docs to a static directory for Docusaurus to use
rm -rf ../../static-sphinx || true
mkdir -p ../../static-sphinx
mv _build/html ../../static-sphinx/docs
rm -rf _build
make markdown SPHINXOPTS="-W --keep-going"
popd

# Move rendered Sphinx markdown to Docusaurus
python "$BASE_DIR/.ci_scripts/sphinx_markdown_to_docusaurus.py" "$BASE_DIR/sphinx/src/_build/markdown" docs/
mkdir -p "$BASE_DIR/static-sphinx/_static"
cp -r "$BASE_DIR/sphinx/src/_static/" "$BASE_DIR/static-sphinx/_static/"
# Build docusaurus site
npm install
npm run build
Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,20 @@ jobs:
publish_dir: ./build
user_name: conda-forge-admin
user_email: pelson.pub+conda-forge@gmail.com

- name: Link Checker
uses: lycheeverse/lychee-action@c053181aa0c3d17606addfe97a9075a32723548a # for v1.9.3
with:
token: ${{ secrets.GITHUB_TOKEN }}
lycheeVersion: '0.14.3'
args: >
--no-progress
--exclude 'https://polys.me/?$'
--exclude 'https://kb43fqob7u-dsn.algolia.net/'
--exclude '.*/404.html/'
--exclude '.*,.*'
--exclude-path './build/docs/orga/minutes/'
--remap "https://conda-forge.org/status https://conda-forge.org/status"
--remap "https://conda-forge.org/(.*) file://$(pwd)/build/\$1"
'./build/**/*.html'
'*.md'
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ sphinx/newsfeed/demo/_build

# Sphinx output gets moved to Docusaurus' static folder
/static-sphinx
/docs

# Docusaurus dependencies
/node_modules
Expand All @@ -42,3 +43,7 @@ sphinx/newsfeed/demo/_build
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# pyc files
*.pyc
__pycache__/
Loading

0 comments on commit 5040828

Please sign in to comment.