Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions great_docs/_versioned_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -1260,6 +1260,13 @@ def _rewrite_quarto_yml_for_version(
# (it defaults to _site anyway, but be explicit)
config.setdefault("project", {})["output-dir"] = "_site"

# Adjust site-url for non-latest versions: append the /v/<tag>/ prefix
# so that Quarto generates correct root-relative asset paths for versioned subpaths.
existing_site_url = config.get("website", {}).get("site-url")
if entry.tag != latest_tag and not entry.latest and existing_site_url:
base = existing_site_url.rstrip("/")
config.setdefault("website", {})["site-url"] = f"{base}/v/{entry.tag}/"

# Set a version-specific title suffix
if entry.tag != latest_tag and not entry.latest:
title = config.get("website", {}).get("title", "")
Expand Down
8 changes: 8 additions & 0 deletions great_docs/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
"no_auto_exclude": False, # Bypass the built-in AUTO_EXCLUDE list entirely
# GitHub integration
"repo": None, # GitHub repository URL override (e.g., "https://github.com/owner/repo")
# Site URL for subdirectory deployments (sets website.site-url in _quarto.yml)
# e.g., "http://myserver:3838/data-team/mypackage/"
"site_url": None,
"github_style": "widget", # "widget" (shows stars) or "icon"
# Source link configuration
"source": {
Expand Down Expand Up @@ -497,6 +500,11 @@ def repo(self) -> str | None:
"""Get the GitHub repository URL override."""
return self.get("repo")

@property
def site_url(self) -> str | None:
"""Get the site URL for subdirectory deployments."""
return self.get("site_url")

@property
def github_style(self) -> str:
"""Get the GitHub link style."""
Expand Down
5 changes: 5 additions & 0 deletions great_docs/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -10055,6 +10055,10 @@ def _update_quarto_config(self) -> None:
if "website" not in config:
config["website"] = {}

# Set site-url for subdirectory deployments (from great-docs.yml)
if self._config.site_url:
config["website"]["site-url"] = self._config.site_url

# Enable page navigation for TOC
if "page-navigation" not in config["website"]:
config["website"]["page-navigation"] = True
Expand Down Expand Up @@ -13532,6 +13536,7 @@ def _on_renders_done() -> None:
quarto_env=quarto_env,
version_tags=version_tags,
latest_only=latest_only,
site_url=self._config.site_url,
progress_callback=_progress_cb,
on_renders_done=_on_renders_done,
badge_expiry_raw=self._config.get("new_is_old"),
Expand Down
65 changes: 65 additions & 0 deletions tests/test_great_docs.py
Original file line number Diff line number Diff line change
Expand Up @@ -15497,6 +15497,71 @@ def test_update_quarto_config_version_badge_no_releases():
assert not meta_path.exists()


def test_update_quarto_config_site_url():
"""Test _update_quarto_config injects site-url from great-docs.yml."""

with tempfile.TemporaryDirectory() as tmp_dir:
docs = GreatDocs(project_path=tmp_dir)

pyproject = Path(tmp_dir) / "pyproject.toml"
pyproject.write_text('[project]\nname = "mypkg"\n', encoding="utf-8")

gd_yml = Path(tmp_dir) / "great-docs.yml"
gd_yml.write_text(
'site_url: "http://myserver:3838/data-team/mypackage/"\n',
encoding="utf-8",
)
docs._config = Config(Path(tmp_dir))

quarto_yml = docs.project_path / "_quarto.yml"
quarto_yml.parent.mkdir(parents=True, exist_ok=True)

base_config = {
"project": {"type": "website", "resources": []},
"website": {"navbar": {"left": []}, "sidebar": []},
"format": {"html": {}},
}

with open(quarto_yml, "w") as f:
write_yaml(base_config, f)

docs._update_quarto_config()

with open(quarto_yml, "r") as f:
result = read_yaml(f)

assert result["website"]["site-url"] == "http://myserver:3838/data-team/mypackage/"


def test_update_quarto_config_site_url_not_set():
"""Test _update_quarto_config does not inject site-url when not configured."""

with tempfile.TemporaryDirectory() as tmp_dir:
docs = GreatDocs(project_path=tmp_dir)

pyproject = Path(tmp_dir) / "pyproject.toml"
pyproject.write_text('[project]\nname = "mypkg"\n', encoding="utf-8")

quarto_yml = docs.project_path / "_quarto.yml"
quarto_yml.parent.mkdir(parents=True, exist_ok=True)

base_config = {
"project": {"type": "website", "resources": []},
"website": {"navbar": {"left": []}, "sidebar": []},
"format": {"html": {}},
}

with open(quarto_yml, "w") as f:
write_yaml(base_config, f)

docs._update_quarto_config()

with open(quarto_yml, "r") as f:
result = read_yaml(f)

assert "site-url" not in result["website"]


def test_create_index_from_readme_citation_parsing():
"""Test _create_index_from_readme produces citation.qmd from CITATION.cff."""

Expand Down
76 changes: 76 additions & 0 deletions tests/test_versioned_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,82 @@ def test_no_site_url_no_canonical(self, tmp_path: Path):
assert "canonical" not in content


# ---------------------------------------------------------------------------
# site-url adjustment for versioned builds
# ---------------------------------------------------------------------------


class TestSiteUrlVersionAdjustment:
def test_non_latest_adjusts_site_url(self, tmp_path: Path):
"""Non-latest versions get site-url with /v/<tag>/ appended."""
from great_docs._versioned_build import _rewrite_quarto_yml_for_version

dest = tmp_path / "v02"
dest.mkdir()
(dest / "_quarto.yml").write_text(
"project:\n type: website\n output-dir: _site\n"
"format:\n html: {}\nwebsite:\n title: Test\n"
" site-url: 'http://myserver:3838/data-team/mypkg/'\n",
encoding="utf-8",
)
entry = _make_entry("0.2")
_rewrite_quarto_yml_for_version(
dest, entry, "0.3", site_url="http://myserver:3838/data-team/mypkg/"
)

from yaml12 import read_yaml

with open(dest / "_quarto.yml") as f:
result = read_yaml(f)

assert result["website"]["site-url"] == "http://myserver:3838/data-team/mypkg/v/0.2/"

def test_latest_keeps_site_url_unchanged(self, tmp_path: Path):
"""Latest version keeps the original site-url as-is."""
from great_docs._versioned_build import _rewrite_quarto_yml_for_version

dest = tmp_path / "root"
dest.mkdir()
(dest / "_quarto.yml").write_text(
"project:\n type: website\n output-dir: _site\n"
"format:\n html: {}\nwebsite:\n title: Test\n"
" site-url: 'http://myserver:3838/data-team/mypkg/'\n",
encoding="utf-8",
)
entry = _make_entry("0.3", latest=True)
_rewrite_quarto_yml_for_version(
dest, entry, "0.3", site_url="http://myserver:3838/data-team/mypkg/"
)

from yaml12 import read_yaml

with open(dest / "_quarto.yml") as f:
result = read_yaml(f)

assert result["website"]["site-url"] == "http://myserver:3838/data-team/mypkg/"

def test_no_site_url_in_config_no_adjustment(self, tmp_path: Path):
"""When site-url is not in _quarto.yml, nothing is injected."""
from great_docs._versioned_build import _rewrite_quarto_yml_for_version

dest = tmp_path / "v02"
dest.mkdir()
(dest / "_quarto.yml").write_text(
"project:\n type: website\n output-dir: _site\n"
"format:\n html: {}\nwebsite:\n title: Test\n",
encoding="utf-8",
)
entry = _make_entry("0.2")
_rewrite_quarto_yml_for_version(dest, entry, "0.3")

from yaml12 import read_yaml

with open(dest / "_quarto.yml") as f:
result = read_yaml(f)

assert "site-url" not in result.get("website", {})


# ---------------------------------------------------------------------------
# Snapshot cache path
# ---------------------------------------------------------------------------
Expand Down
3 changes: 3 additions & 0 deletions user_guide/05-configuration.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,9 @@ exclude:
repo: https://github.com/your-org/your-package # Override auto-detect
github_style: widget

# Site URL (for subdirectory deployments)
# site_url: "https://internal.example.com/docs/mypackage/"

# Source Links
source:
enabled: true
Expand Down
43 changes: 43 additions & 0 deletions user_guide/14-deployment.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,49 @@ The built site is in `great-docs/_site/`. Upload this directory to your hosting
- **AWS S3**: Sync the `great-docs/_site/` folder to your bucket
- **Any static host**: Copy contents of `great-docs/_site/`

## Subdirectory Deployments

If your site is hosted at a subpath rather than a domain root (for example,
`https://internal.example.com/docs/mypackage/`), you need to tell Quarto the base URL so it
generates correct root-relative paths for CSS, JS, and navigation links.

Set `site_url` in `great-docs.yml`:

```{.yaml filename="great-docs.yml"}
site_url: "https://internal.example.com/docs/mypackage/"
```

This writes `website.site-url` into the generated `_quarto.yml` during every
build, so you never have to patch the config manually.

### What it fixes

Without `site_url`, Quarto generates root-relative asset paths like `/site_libs/...` and
`/reference/...`. When the site lives at a subpath, those paths resolve to the wrong location and
the site appears broken (missing styles, broken navigation, 404s on the reference section, etc.).

### Versioned sites

For multi-version builds, Great Docs automatically appends the version prefix to `site-url` for
non-latest versions. If your `site_url` is:

```
https://internal.example.com/docs/mypackage/
```

then version `0.2` (served at `/v/0.2/`) will use:

```
https://internal.example.com/docs/mypackage/v/0.2/
```

No extra configuration is needed.

### When you don't need it

If you deploy to a domain root (e.g., `https://yourpackage.github.io/` or a custom domain), you do
not need to set `site_url`. Quarto's defaults work correctly in that case.

## Custom Domain

To use a custom domain with GitHub Pages:
Expand Down
Loading