Repository navigation
docs(py): build the Python site with great-docs, with versioned deploys - #373
Merged
Merged
Conversation
great-docs takes the navbar version badge from the newest GitHub Release for the whole repository and cannot filter by tag prefix, so in this monorepo it shows whichever of the two release series published most recently. The only Release so far is r-v0.1.0, which rendered as vr-v0.1.0 on the Python site. The badge is injected during great-docs' own post-render step, so it cannot be corrected mid-build. This rewrites it afterwards from pkg-py/pyproject.toml, the same way noindex-preview.py post-processes built HTML.
Quarto has no version selector (quarto-cli#474 is open with milestone Future) and quartodoc has none, so versioned docs on the old toolchain meant hand-rolling a manifest, a dropdown, canonical tags and redirects. great-docs does it from a versions list, and builds on quartodoc rather than replacing it. It wraps Quarto, so Quarto is still what renders the site. The design notes move to pkg-py/notes because great-docs renders everything under the directory user_guide names, and the old explicit Quarto render list that kept them out is gone. The generated changelog is off: it reads GitHub Releases for the whole repository with no tag-prefix filter, so it put the R package's notes on the Python site.
great-docs has no config key for a custom navbar link, does not merge a project _quarto.yml, and rewrites the generated one every build, so the R cross-link is injected with a script next to its own navbar tools. The empty span is what docs/assets/styles.css already turns into the R logo, so only the markup had to be reproduced. The favicon block stays an include rather than becoming the favicon: key, which takes a single file and would drop the manifest and the other sizes that the R site serves from the same shared /favicon/. The custom stylesheet goes under site.css. A top-level css: key is accepted and silently ignored, which leaves the site unstyled. Relative links had to move with the pages. The narrative pages now render under user-guide/, so the homepage's links into them gained that prefix, and the governance page's hops out to the R site became absolute. Relative cross-site links cannot survive versioning anyway: from /py/v/<tag>/ a ../r/ hop lands in /py/v/r/ rather than at the site root. The stylesheet's R logo url() moved for the same reason the hex image's src did, because great-docs copies docs/ to the user-guide/ prefix and surfaces only the navbar logo at the root.
… date With a navbar logo rather than a text title, great-docs puts the version in a Tippy tooltip on the brand link instead of a badge span, so the wrong version reached the page by a second route that the first version of this script did not cover. This repository's config sets a logo, so the tooltip was in fact the only form appearing. The release date is now dropped rather than preserved. It comes from the same GitHub Release as the version, so on the Python site it was the R package's release date, and pyproject.toml carries no date to put in its place. Correcting the version while leaving the date would trade one wrong claim for another.
The version list is hand-maintained so prerelease, eol and label stay editorial rather than inferred from py-v* tags, which would have had to guess that 0.1.0b1 is a prerelease. Adding an entry becomes a release step. 0.1.0b1 takes the latest slot, so /py/ serves what `pip install commons` resolves to, and the development version sits at /py/v/dev/. `latest` is set explicitly because a prerelease is excluded from latest auto-detection and every entry here is one. The announcement banner carries the beta warning, which reaches readers who land on an API reference page rather than the homepage. It also means the visible version in the navbar comes from the selector, built from this list, rather than from the release-derived badge.
Only the build step changes. The copy into docs/py, the shared gh-pages deploy with clean:false, the noindex pass and the pr-<n> previews are untouched, because those are what let the two sites and the previews share one branch. quarto-actions/setup stays: great-docs shells out to Quarto. The release: published trigger is replaced with the py-v* tag push it was presumably meant to be. Releases are cut by pushing a tag, so the old trigger never fired. Verified by running scripts/preview-docs.sh py and serving the result from a pr-<n>/py/ subpath: the stylesheet, the R logo, the hex image, a reference page and a versioned copy all resolve, so the absolute site_url does not break a preview served from a deeper prefix.
AGENTS.md described the published tree without saying what builds the Python half or that it now publishes a copy per release. favicon/README.md pointed at a favicon partial that no longer exists; those tags moved into great-docs.yml, since great-docs' own favicon key takes a single file and would drop the manifest and the other sizes.
A fix to docs-version-badge.py would otherwise wait for an unrelated docs change before reaching the deployed site, because the path filters did not mention it. The preview script runs the helper through uv's interpreter rather than the system python3, matching the line above it that reads the version the same way.
The R cross-link was injected straight into .quarto-navbar-tools, which is empty in the static HTML: great-docs collects its widgets at runtime into a #gd-navbar-widgets flex container inside it. The link sat outside that container and so missed its alignment and gap, leaving the logo floating above the row. It now waits for that container and joins it, carrying the gd-navbar-icon class so it is drawn like the buttons beside it. The hex sticker becomes the configured logo. It was an inline image in index.qmd, which great-docs rendered underneath its own logo, so the landing page showed the python mark above the hex. Reference entries, navbar links and table links drop to normal weight. great-docs sets them in bold monospace, and on a page that is mostly a list of symbol names the weight on top of an underline reads as shouting. The announcement banner paints a mid-blue background but left its text at the body colour, which is close to unreadable in light mode, and an inline code span kept its own pale pill background and disappeared into it.
The author entry gains its ORCID, Posit page and GitHub account. great-docs renders each as an icon link beside the name, which is how pkgdown presents the same thing on the R site; it does not link the name text itself. The hand-written favicon block was broken. Its paths began with a slash, which the old Quarto project resolved against the Python site root, but great-docs puts the narrative source under user-guide/, so pkg-py/docs/favicon/ landed at user-guide/favicon/ and all five links resolved to nothing. Rather than repoint them at that prefix, great-docs now derives the icon set itself from a favicon: source, so the link tags are generated and there is no hand-maintained HTML to drift. That source is the same shared icon the other two sites serve, so a tab still shows the same image everywhere. It names the PNG rather than the SVG on purpose: great-docs rasterizes an SVG with cairosvg, which needs a system cairo library and which warns and skips the raster sizes when absent. Pillow handles the PNG with nothing extra. Two things go with the hand-written block: the favicon.svg link that modern browsers would have preferred, and the site.webmanifest link.
The navbar and the section list both marked the current page with a rule under it. The navbar's own bold had been lost to the earlier weight override, which was not scoped to exclude the active item, and the sidebar item was orange with a thick blue underline, two accent colours the site does not otherwise use. Both now use weight alone. Tables were sized to their widest unwrapped line and then scrolled horizontally. That suits a data table, but the feature-parity table is prose, so it ran past the page rather than wrapping inside it. The previous and next page links were drawn as outlined buttons; they are ordinary links.
The label comes from reference.title, which great-docs only reads when reference is a mapping. As a bare list of sections it was ignored and the entry fell back to "Reference", so the sections move under a sections: key. The current-sidebar-item colour moves behind two custom properties. great-docs hardcodes it in its own stylesheet with no config key, so a CSS override is the only lever and this makes it one value to change rather than a rule to rewrite.
Hovering a navbar link filled it with an orange pill and white text. It is a literal in great-docs.scss with a dark-mode twin at a higher specificity, so both selectors are spelled out. A neutral tint matches the hover already used by the icon buttons beside it. The orange reaches four places in all. Two are the --gd-active-link theme variable, now pointed at currentColor so the dark-mode sidebar and on-this-page active items follow; two are literals needing their selectors overridden. --gd-syn-operator is deliberately untouched, being the operator colour inside code blocks rather than site accent.
The light tint was 6% ink, which is what great-docs uses for the resting state of its navbar icon buttons, not their hover, so on a #f0f0f0 navbar it was all but invisible. Both themes now sit at 14%, which lands them a similar distance from their own background: #cecece against light, #3a3a3a against dark. That is a step past the 10% and 12% great-docs hovers its icon buttons at, because those have a border to help them read and a bare text link does not.
The hover label was set to `inherit`, which takes the colour of the parent list item rather than the link's own, so on the new hover tint it came out almost white. It now names the navbar text colour, which is already theme-aware. The active navbar item still carried a blue underline. There are two underlines on it: a bottom border from .nav-underline, which was already suppressed, and a text decoration from .nav-link.active in the accent blue, which was not. The override block is also consolidated. An earlier edit in this branch left a duplicate of the current-page rule, and the variable declarations had spread across two :root and two body.quarto-dark blocks.
Pointing --gd-active-link at currentColor removed the orange but left the current item inheriting a colour dimmer than its own siblings, so in dark mode it read as the least prominent entry in the list rather than the most. Both the variable and the light-mode override now name --gd-text-primary, which is itself theme-scoped: #e0e0e0 against the #c8c8c8 of the other items, and the darkest text in light mode. The hover pill carries 8px of padding on each side but a -8px left margin, which pulled it flush against the sidebar's left edge while leaving it inset on the right. Dropping the negative margin makes the inset even.
The guide was ordered alphabetically, so it opened on the feature-parity table and the navbar entry pointed there too. Numeric filename prefixes set the order in auto-discovery mode and great-docs strips them when it copies the pages, so the published URLs are unchanged and the navbar follows the first file. The explicit user_guide list was the other option and was rejected: it only reads the conventional user_guide/ directory, which would have meant moving the pages out of docs/ and repointing the asset paths that the stylesheet and the favicon source depend on, and it requires a section heading the sidebar does not currently have.
Josh's wording, taken as written.
The banner is built client-side from a meta tag and its url is used verbatim, so Quarto never rewrites it per page depth. A relative link therefore resolved against the current page's directory: from user-guide/ the banner asked for user-guide/user-guide/feature-parity.html. It was broken on 84 of the 90 pages that carry it, everything but the six at the site root. Quarto's own offset meta supplies the prefix, which is how great-docs' version selector and sidebar scripts locate the site root, and it stays correct in a pull request preview and inside a versioned copy. An absolute or root-relative url would have sent preview traffic to the published site instead. The link is found by a class substring rather than by name because great-docs drops every include_in_header entry whose text contains its own "gd-announcement" marker, which removed an earlier version of this script without any warning.
The previous attempt never ran to completion. Assigning to a.href reflects the
resolved absolute URL into the content attribute, so by the time the script
read getAttribute("href") it saw the already-doubled absolute path, which
tripped the guard that skips links needing no prefix. The raw relative path is
now read back from the announcement meta, which is the only place it survives
unresolved.
Verified in Chromium rather than by reasoning about the emitted markup:
the banner link is followed from the landing page, two guide pages, the
reference index, a reference detail page and a versioned copy, and every one
returns 200 on the feature-parity page, with the versioned copy correctly
staying inside its own /v/dev/ tree.
The navbar carried only the hex at 24px, which is too small to read as a sticker and left the site unnamed. It now shows the Python mark beside "commons", as the R site shows the R mark beside it. show_title is required: great-docs suppresses the navbar's text title whenever a logo is set. The hex moves to hero.logo, so the landing page keeps it at full size. That key otherwise inherits the navbar logo, which is why the two had to be set separately rather than one replacing the other.
The favicon links were written against site_url, so every page pointed at the published site and no icon appeared anywhere else: not locally, not in a pull request preview, not in a versioned copy. The generated icons sit at the root of each copy, so Quarto's per-page offset is the right prefix. These were the only absolute URLs in the built site. The package name was drawn as a bordered pill with the version beside it. The border is gone and the badge is now stripped rather than corrected, because the version selector already names the version and is driven by the list in great-docs.yml rather than by a repo-wide GitHub Release lookup. With the navbar title shown, the badge is the only place great-docs puts a version, so nothing is left showing the wrong one and the correction has nothing left to do. The hero logo was a broken image: great-docs emits hero.logo as the img src verbatim and, unlike the navbar logo, does not copy the file, so the path now names where the asset actually lands. docs-version-badge.py becomes docs-postprocess.py, since relativizing the icon links is the job that remains and the badge is removed in the same pass.
The rewrite replaced the site URL in every quoted string, not just favicon hrefs. great-docs also injects a canonical-URL script that concatenates the site URL with a path at runtime, so rewriting it to a page-relative prefix would have produced a bogus canonical URL on every non-latest version. It escaped only because that script omits the trailing slash the pattern required, which is not a property worth relying on. Now only <link> tags are touched, with regression tests for both spellings of the script's base. The author entry declared github twice. PyYAML keeps the last of a duplicate key, so it worked, but stricter parsers reject it.
Contributor
|
Preview root: https://posit-dev.github.io/commons/pr-373/ Python site preview: https://posit-dev.github.io/commons/pr-373/py/ Built from the latest commit on this branch. The R links in it point at the published R site, which no pull request rebuilds. |
The offset fix held locally and on the published site but not in a pull request preview, where the banner link 404ed again. Quarto's external-link pass runs after it and restores link.dataset.originalHref, which quarto-nav.js had set to the banner's original un-prefixed url, undoing the correction. That pass only treats this link as external when the served prefix differs from site_url, which is true of every preview and of neither localhost nor the published site, so no local check could have caught it. The link now carries Quarto's own .no-external opt-out, which excludes it from that pass, and the stashed original is updated to the corrected path so the restore is harmless if it runs anyway.
The links are built relative to the package root, which in this monorepo is pkg-py/ rather than the repository root, so every "source" button pointed at a path that does not exist. great-docs has a source.path key for exactly this, and it cannot be used: it replaces the whole path with the file's basename, which is right for a flat layout but flattens commons/_ui/_server.py and the three others like it. The package directory is inserted after rendering instead, which preserves the real layout. Verified against the repository rather than by eye: all nine distinct source paths the reference emits exist under pkg-py, and the generated URL for Commons resolves on both this branch and main.
…repo The workflow file was renamed to py-docs.yaml, which left its own paths filter naming a file that no longer exists, so a change to the workflow stopped triggering it. The job id and concurrency group still said quartodoc, as did comments in three sibling workflows and in the preview script. main has no branch protection, so renaming the job breaks no required check. The source-link rewrite is now scoped to this repository. It matched any GitHub blob URL with a src/ path, so a documentation link into another project's source tree would have gained a pkg-py/ prefix. The repository URL comes from the same config the site URL does.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR moves the Python documentation site from
quartodocto great-docs, which gives it versioned deploys with a version selector, the version in the navbar, and a beta notice on every page. Only the build step of the docs workflow changes; the sharedgh-pageslayout, the deploy, and the pull request previews are untouched.kata: fspr. Supersedes most of kata 2f1g.
Merge #372 first. It bumps
pkg-pyto0.1.0b1, which this branch declares as the latest documented version while its ownpyproject.tomlis still0.1.0.dev1. Nothing else depends on the ordering.Agent-written detail
Why not build this on Quarto
Quarto has no version selector (quarto-cli#474 is open, milestone "Future") and quartodoc has none, so versioned docs on the old toolchain meant a hand-built manifest, a dropdown, canonical tags and per-page redirects. great-docs added it in v0.8, and builds on quartodoc rather than replacing it. It wraps Quarto, so Quarto still renders the site and
quarto-actions/setupstays in the workflow.What the site does now
0.1.0b1is served at/py/, the development version at/py/v/dev/, with/py/v/latest/and/py/v/stable/aliases and a navbar selector. Theversions:list is hand-maintained rather than derived from tags, soprerelease,eoland labels stay editorial and reviewable; adding an entry becomes a step in cutting a release, andgreat-docs api-snapshot <tag>can backfill a version's API pages from a git tag afterwards.Versioned narrative pages are re-rendered from the current tree, not archived, so editing a guide page changes what older versions say. Only the API reference is snapshotted.
Things that needed working around
ui.app, notcommons.ui.app. A section-levelpackage:key parses and is then ignored.commons.__all__does not exportui, so those five functions only appear because they are listed._quarto.ymlis not merged, and the generated one is rewritten every build, so the R cross-link is injected by a script into great-docs' own widget container. It is selected by class substring because great-docs silently drops anyinclude_in_headerentry whose text contains one of itsgd-*markers.site_url, so no icon resolved outside the published site. They are the only absolute URLs in the build and are relativized after rendering.setup-github-pagesis not used. It targets the Actions Pages source, which owns the whole site and would take/r/and/r/dev/offline, and it replaces hosted previews with a local-build comment.Layout and styling
Design notes moved to
pkg-py/notes/, because great-docs renders everything under the directoryuser_guide:names and the explicit Quarto render list that kept them out is gone. Guide pages carry numeric filename prefixes to set their order, which great-docs strips, so the published URLs are unchanged and the navbar lands on the introduction. The synceddocs/assets/anddocs/favicon/copies stay wherescripts/sync-shared.shputs them.The stylesheet overrides great-docs' bold reference entries, its orange current-page and hover accents, its outlined page-navigation buttons, and a table rule that sized the feature-parity table to its widest unwrapped line. Two custom properties at the top of the file are the knobs for the accent treatment.
Verification
ruff,pyreflyand the 1499 Python tests pass, as do both.github/scriptssuites. The site was checked in headless Chromium rather than by reading the markup: the banner link is followed from the landing page, guide pages, the reference index, a reference detail page and a versioned copy, and the favicons resolve at three depths and inside/v/dev/. A build served from apr-<n>/py/prefix resolves its assets, which was the open risk in the design.roborev compactre-verified every finding raised on the branch against the final tree and reported none outstanding.