From b5bfa5310d6a33c0a72b6b27f12a2add031ea1dd Mon Sep 17 00:00:00 2001 From: Remco de Boer <29308176+redeboer@users.noreply.github.com> Date: Thu, 6 Aug 2026 15:41:32 +0200 Subject: [PATCH 1/2] FEAT: update `linkcheck` job for Quarto projects --- src/compwa_policy/repo/poe.py | 20 ++++++++ src/compwa_policy/utilities/toml.py | 2 +- tests/repo/test_poe.py | 75 +++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/src/compwa_policy/repo/poe.py b/src/compwa_policy/repo/poe.py index 28df5ad1..fa07eb97 100644 --- a/src/compwa_policy/repo/poe.py +++ b/src/compwa_policy/repo/poe.py @@ -76,6 +76,7 @@ def check(session: Session, args: Arguments, ctx: CheckContext) -> None: _set_doc_group(config) _set_test_group(config) _set_notebook_group(config, ctx.has_notebooks) + _set_quarto_linkcheck(config) _check_no_uv_run(config) if config.has_table("tool.poe.tasks"): _set_all_task(config) @@ -212,6 +213,25 @@ def _set_notebook_group(pyproject: ModifiablePyproject, /, has_notebooks: bool) pyproject.changelog.append(msg) +def _set_quarto_linkcheck(pyproject: ModifiablePyproject, /) -> None: + if not is_committed("_quarto.yml", "**/_quarto.yml", ":!:tests", untracked=True): + return + pyproject.add_dependency("lychee-bin", dependency_group="doc") + tasks = _get_or_create_group_tasks(pyproject, "doc") + existing = cast("Mapping", tasks.get("linkcheck", {})) + if "lychee" in existing.get("cmd", ""): + return + expected = { + "cmd": "lychee --root-dir . . && lychee --root-dir . --extensions qmd .", + "executor": to_inline_table({"group": "doc"}), + "help": "Check external links in the documentation (requires internet connection)", + } + if existing != expected: + tasks["linkcheck"] = expected + msg = f"Set Poe the Poet linkcheck task in {CONFIG_PATH.pyproject}" + pyproject.changelog.append(msg) + + def _check_no_uv_run(pyproject: Pyproject) -> None: poe_table = pyproject.get_table("tool.poe") all_task_tables: list[Mapping] = [ diff --git a/src/compwa_policy/utilities/toml.py b/src/compwa_policy/utilities/toml.py index 83c76b3f..6bba87ba 100644 --- a/src/compwa_policy/utilities/toml.py +++ b/src/compwa_policy/utilities/toml.py @@ -23,7 +23,7 @@ def to_toml_array(items: Iterable[Any], multiline: bool | None = None) -> Array: return array -def to_inline_table(value: Mapping[str, Any]) -> InlineTable: +def to_inline_table(value: Mapping[str, Any], /) -> InlineTable: table = tomlkit.inline_table() if value: table.append(None, tomlkit.ws(" ")) diff --git a/tests/repo/test_poe.py b/tests/repo/test_poe.py index 98d3118b..cbc2eff6 100644 --- a/tests/repo/test_poe.py +++ b/tests/repo/test_poe.py @@ -111,6 +111,81 @@ def is_noop_without_pyproject( # no pyproject run_check(check, session, has_notebooks=False, package_manager="uv") + def configures_lychee_for_quarto( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + git_init: Callable[[Path], None], + git_add: Callable[[Path], None], + run_check, + ): + git_init(tmp_path) + (tmp_path / "_quarto.yml").touch() + (tmp_path / "index.qmd").touch() + (tmp_path / "pyproject.toml").write_text( + dedent(""" + [dependency-groups] + dev = [{ include-group = "doc" }] + doc = [] + + [tool.poe.tasks.doc] + cmd = "quarto render" + + [tool.poe.tasks.doclive] + cmd = "quarto preview" + + [tool.poe.tasks.linkcheck] + cmd = "echo linkcheck-not-supported" + """).lstrip() + ) + git_add(tmp_path) + monkeypatch.chdir(tmp_path) + with Session.load() as session: + run_check(check, session, has_notebooks=False, package_manager="uv") + pyproject = Pyproject.load(tmp_path / "pyproject.toml") + assert "lychee-bin" in pyproject.get_table("dependency-groups.doc") + linkcheck = pyproject.get_table("tool.poe.groups.doc.tasks.linkcheck") + assert linkcheck == { + "cmd": "lychee --root-dir . . && lychee --root-dir . --extensions qmd .", + "executor": {"group": "doc"}, + "help": ( + "Check external links in the documentation (requires internet connection)" + ), + } + + def preserves_custom_lychee_extensions( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + git_init: Callable[[Path], None], + git_add: Callable[[Path], None], + run_check, + ): + git_init(tmp_path) + (tmp_path / "_quarto.yml").touch() + config_path = tmp_path / "pyproject.toml" + config_path.write_text( + dedent(""" + [dependency-groups] + doc = [] + + [tool.poe.tasks.doc] + cmd = "quarto render" + + [tool.poe.tasks.doclive] + cmd = "quarto preview" + + [tool.poe.tasks.linkcheck] + cmd = "lychee --extensions md,qmd,typ ." + """).lstrip() + ) + git_add(tmp_path) + monkeypatch.chdir(tmp_path) + with Session.load() as session: + run_check(check, session, has_notebooks=False, package_manager="uv") + linkcheck = Pyproject.load(config_path).get_table( + "tool.poe.groups.doc.tasks.linkcheck" + ) + assert linkcheck["cmd"] == "lychee --extensions md,qmd,typ ." + def describe_update_doclive(): def adds_executor(): From 2a26aca3c242ed642c8e6a39df9731675e18bdee Mon Sep 17 00:00:00 2001 From: Remco de Boer <29308176+redeboer@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:22:57 +0200 Subject: [PATCH 2/2] FIX: use correct shell command --- src/compwa_policy/repo/poe.py | 4 ++-- tests/repo/test_poe.py | 36 ++++++++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/compwa_policy/repo/poe.py b/src/compwa_policy/repo/poe.py index fa07eb97..930b0b6c 100644 --- a/src/compwa_policy/repo/poe.py +++ b/src/compwa_policy/repo/poe.py @@ -219,12 +219,12 @@ def _set_quarto_linkcheck(pyproject: ModifiablePyproject, /) -> None: pyproject.add_dependency("lychee-bin", dependency_group="doc") tasks = _get_or_create_group_tasks(pyproject, "doc") existing = cast("Mapping", tasks.get("linkcheck", {})) - if "lychee" in existing.get("cmd", ""): + if "lychee" in existing.get("cmd", "") or "lychee" in existing.get("shell", ""): return expected = { - "cmd": "lychee --root-dir . . && lychee --root-dir . --extensions qmd .", "executor": to_inline_table({"group": "doc"}), "help": "Check external links in the documentation (requires internet connection)", + "shell": "lychee --root-dir . . && lychee --root-dir . --extensions qmd .", } if existing != expected: tasks["linkcheck"] = expected diff --git a/tests/repo/test_poe.py b/tests/repo/test_poe.py index cbc2eff6..96eebc8b 100644 --- a/tests/repo/test_poe.py +++ b/tests/repo/test_poe.py @@ -145,11 +145,11 @@ def configures_lychee_for_quarto( assert "lychee-bin" in pyproject.get_table("dependency-groups.doc") linkcheck = pyproject.get_table("tool.poe.groups.doc.tasks.linkcheck") assert linkcheck == { - "cmd": "lychee --root-dir . . && lychee --root-dir . --extensions qmd .", "executor": {"group": "doc"}, "help": ( "Check external links in the documentation (requires internet connection)" ), + "shell": "lychee --root-dir . . && lychee --root-dir . --extensions qmd .", } def preserves_custom_lychee_extensions( @@ -186,6 +186,40 @@ def preserves_custom_lychee_extensions( ) assert linkcheck["cmd"] == "lychee --extensions md,qmd,typ ." + def preserves_custom_lychee_shell( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + git_init: Callable[[Path], None], + git_add: Callable[[Path], None], + run_check, + ): + git_init(tmp_path) + (tmp_path / "_quarto.yml").touch() + config_path = tmp_path / "pyproject.toml" + config_path.write_text( + dedent(""" + [dependency-groups] + doc = [] + + [tool.poe.tasks.doc] + cmd = "quarto render" + + [tool.poe.tasks.doclive] + cmd = "quarto preview" + + [tool.poe.tasks.linkcheck] + shell = "lychee . && lychee --extensions qmd ." + """).lstrip() + ) + git_add(tmp_path) + monkeypatch.chdir(tmp_path) + with Session.load() as session: + run_check(check, session, has_notebooks=False, package_manager="uv") + linkcheck = Pyproject.load(config_path).get_table( + "tool.poe.groups.doc.tasks.linkcheck" + ) + assert linkcheck["shell"] == "lychee . && lychee --extensions qmd ." + def describe_update_doclive(): def adds_executor():