diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml index 4813cc73b..f42d877ee 100644 --- a/.github/workflows/build-and-publish.yml +++ b/.github/workflows/build-and-publish.yml @@ -23,7 +23,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: "3.10" poetry-version: "2.3.0" diff --git a/.github/workflows/check-release-tag.yml b/.github/workflows/check-release-tag.yml index 2191c3057..0e3971ab1 100644 --- a/.github/workflows/check-release-tag.yml +++ b/.github/workflows/check-release-tag.yml @@ -21,7 +21,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: "3.10" poetry-version: "2.3.0" diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 2c0dc355e..d247a7e29 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -20,7 +20,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: "3.10" poetry-version: "2.3.0" @@ -48,7 +48,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: "3.10" poetry-version: "2.3.0" @@ -75,7 +75,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: ${{ matrix.python-versions }} poetry-version: "2.3.0" @@ -113,7 +113,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: ${{ matrix.python-versions }} poetry-version: "2.3.0" @@ -141,7 +141,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: ${{ matrix.python-versions }} poetry-version: "2.3.0" @@ -173,7 +173,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: "3.10" poetry-version: "2.3.0" @@ -196,7 +196,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: "3.10" poetry-version: "2.3.0" @@ -219,7 +219,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: "3.10" poetry-version: "2.3.0" @@ -242,7 +242,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: "3.10" poetry-version: "2.3.0" diff --git a/.github/workflows/dependency-update.yml b/.github/workflows/dependency-update.yml index 62cbc9c53..2945c41ee 100644 --- a/.github/workflows/dependency-update.yml +++ b/.github/workflows/dependency-update.yml @@ -35,7 +35,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: "3.10" poetry-version: "2.3.0" diff --git a/.github/workflows/fast-tests-extension.yml b/.github/workflows/fast-tests-extension.yml index 88f645cdc..135889564 100644 --- a/.github/workflows/fast-tests-extension.yml +++ b/.github/workflows/fast-tests-extension.yml @@ -18,7 +18,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v8 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: "3.10" poetry-version: "2.3.0" diff --git a/.github/workflows/fast-tests.yml b/.github/workflows/fast-tests.yml index ac7faa679..3958495e7 100644 --- a/.github/workflows/fast-tests.yml +++ b/.github/workflows/fast-tests.yml @@ -25,7 +25,7 @@ jobs: fetch-depth: 0 - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: ${{ matrix.python-versions }} poetry-version: "2.3.0" diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 134be7f44..ff05877bf 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -22,7 +22,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: "3.10" poetry-version: "2.3.0" diff --git a/.github/workflows/matrix.yml b/.github/workflows/matrix.yml index c0476ae0c..de338684a 100644 --- a/.github/workflows/matrix.yml +++ b/.github/workflows/matrix.yml @@ -28,7 +28,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: "3.10" poetry-version: "2.3.0" diff --git a/.github/workflows/report.yml b/.github/workflows/report.yml index 621a6447d..5df42e9e6 100644 --- a/.github/workflows/report.yml +++ b/.github/workflows/report.yml @@ -24,7 +24,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v9 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: "3.10" poetry-version: "2.3.0" diff --git a/.github/workflows/slow-checks.yml b/.github/workflows/slow-checks.yml index 9b040ed10..895efe30f 100644 --- a/.github/workflows/slow-checks.yml +++ b/.github/workflows/slow-checks.yml @@ -33,7 +33,7 @@ jobs: - name: Set up Python & Poetry Environment id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v8 + uses: exasol/python-toolbox/.github/actions/python-environment@v10 with: python-version: ${{ matrix.python_versions }} poetry-version: "2.3.0" diff --git a/doc/changes/unreleased.md b/doc/changes/unreleased.md index fb4737052..163bd2a0c 100644 --- a/doc/changes/unreleased.md +++ b/doc/changes/unreleased.md @@ -1,3 +1,12 @@ # Unreleased ## Summary + +This minor release adds an opt-out for documentation-enabled workflows and jobs. Projects can +now declare that they do not serve documentation with the `has_documentation` switch in +`BaseConfig`, and the workflow generator/checker will stop documentation relevant jobs +and workflows for those projects. This should only be set to `False` for exceptional cases. + +## Feature + +* #901: Provided switch `has_documentation` in `BaseConfig` for projects without documentation diff --git a/doc/user_guide/features/github_workflows/create_and_update.rst b/doc/user_guide/features/github_workflows/create_and_update.rst index 16e63840e..da0edb4df 100644 --- a/doc/user_guide/features/github_workflows/create_and_update.rst +++ b/doc/user_guide/features/github_workflows/create_and_update.rst @@ -85,6 +85,17 @@ used when you :ref:`install or update your workflows `. generated, each removed job or modified step_id is checked to see if it exists. If it does not exist, an exception will be raised (:ref:`workflow_exceptions`). +Projects Without Documentation +------------------------------ + +Most projects using the PTB should serve Sphinx-based documentation. For +exceptional cases where a project does not serve documentation, set +:py:attr:`exasol.toolbox.config.BaseConfig.has_documentation` to ``False`` in +the project configuration. + +When this switch is disabled, the PTB no longer generates or expects documentation +relevant parts in the GitHub workflows. + .. _update_workflows: Add all Workflows to Your Project diff --git a/exasol/toolbox/config.py b/exasol/toolbox/config.py index de221b329..38bcd5169 100644 --- a/exasol/toolbox/config.py +++ b/exasol/toolbox/config.py @@ -211,6 +211,17 @@ class BaseConfig(BaseModel): """, ) + model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True) + + @computed_field # type: ignore[misc] + @property + def has_documentation(self) -> bool: + """ + Indicates that the project serves Sphinx-based documentation. With a few + exceptions, this should be the case for most projects. + """ + return True + @computed_field # type: ignore[misc] @property def sonar_token_name(self) -> str: @@ -221,8 +232,6 @@ def sonar_token_name(self) -> str: """ return "SONAR_TOKEN" - model_config = ConfigDict(frozen=True, arbitrary_types_allowed=True) - @computed_field # type: ignore[misc] @property def documentation_path(self) -> Path: @@ -327,6 +336,7 @@ def github_template_dict(self) -> dict[str, Any]: return { "custom_workflows": custom_workflow_extractor.build_custom_workflow_dict(), "dependency_manager_version": self.dependency_manager.version, + "has_documentation": self.has_documentation, "minimum_python_version": self.minimum_python_version, "os_version": self.os_version, "python_versions": self.python_versions, diff --git a/exasol/toolbox/templates/github/workflows/cd.yml b/exasol/toolbox/templates/github/workflows/cd.yml index ca8ec258f..55fd7ae91 100644 --- a/exasol/toolbox/templates/github/workflows/cd.yml +++ b/exasol/toolbox/templates/github/workflows/cd.yml @@ -40,6 +40,7 @@ jobs: contents: write (% endif %) + (% if has_documentation %) publish-docs: needs: - build-and-publish @@ -49,3 +50,4 @@ jobs: contents: read pages: write id-token: write + (% endif %) \ No newline at end of file diff --git a/exasol/toolbox/templates/github/workflows/checks.yml b/exasol/toolbox/templates/github/workflows/checks.yml index c166d76aa..017dfb070 100644 --- a/exasol/toolbox/templates/github/workflows/checks.yml +++ b/exasol/toolbox/templates/github/workflows/checks.yml @@ -5,6 +5,7 @@ on: workflow_call: jobs: + (% if has_documentation %) build-documentation-and-check-links: name: Docs runs-on: "(( os_version ))" @@ -31,6 +32,7 @@ jobs: - name: Check Links id: check-links run: poetry run -- nox -s links:check + (% endif %) check-changelog-was-updated: name: Check Changelog was Updated diff --git a/exasol/toolbox/util/workflows/templates.py b/exasol/toolbox/util/workflows/templates.py index 8811fc9e6..12efa2377 100644 --- a/exasol/toolbox/util/workflows/templates.py +++ b/exasol/toolbox/util/workflows/templates.py @@ -1,5 +1,6 @@ from collections.abc import Mapping from pathlib import Path +from typing import Final import importlib_resources as resources @@ -9,7 +10,8 @@ ) WORKFLOW_TEMPLATES_DIRECTORY = "exasol.toolbox.templates.github.workflows" -NOT_MAINTAINED_WORKFLOW_NAMES: list[str] = ["slow-checks"] +NOT_MAINTAINED_WORKFLOW_NAMES: Final[list[str]] = ["slow-checks"] +DOCUMENTATION_ONLY_WORKFLOW_NAMES: Final[list[str]] = ["pr-merge"] def get_workflow_templates() -> Mapping[str, Path]: diff --git a/exasol/toolbox/util/workflows/workflow_orchestrator.py b/exasol/toolbox/util/workflows/workflow_orchestrator.py index 0182cd12e..646653aac 100644 --- a/exasol/toolbox/util/workflows/workflow_orchestrator.py +++ b/exasol/toolbox/util/workflows/workflow_orchestrator.py @@ -25,6 +25,7 @@ WorkflowPatcher, ) from exasol.toolbox.util.workflows.templates import ( + DOCUMENTATION_ONLY_WORKFLOW_NAMES, WORKFLOW_TEMPLATE_OPTIONS, validate_workflow_name, ) @@ -110,7 +111,7 @@ def _load_workflow( def _skip_workflow(self, workflow_name: str, is_new_project: bool) -> bool: """ Return ``True`` if the workflow should be skipped because it is not maintained - by the PTB, otherwise return ``False``. + by the PTB or not applicable to the current project, otherwise return ``False``. """ try: validate_workflow_name(workflow_name) @@ -121,6 +122,17 @@ def _skip_workflow(self, workflow_name: str, is_new_project: bool) -> bool: workflow_name, ) return True + + if ( + workflow_name in DOCUMENTATION_ONLY_WORKFLOW_NAMES + and not self.config.has_documentation + ): + logger.debug( + "Skipping documentation workflow for project without documentation: %s", + workflow_name, + ) + return True + return False def generate_workflows(self) -> None: diff --git a/test/unit/config_test.py b/test/unit/config_test.py index 77a370253..580b816cb 100644 --- a/test/unit/config_test.py +++ b/test/unit/config_test.py @@ -44,6 +44,7 @@ def test_works_as_defined(tmp_path, test_project_config_factory): "create_major_version_tags": False, "dependency_manager": {"name": "poetry", "version": "2.3.0"}, "documentation_path": root_path / "doc", + "has_documentation": True, "exasol_versions": ("8.29.13", "2025.1.8"), "excluded_python_paths": expand_paths(config, DEFAULT_EXCLUDED_PATHS), "github_workflow_directory": tmp_path / ".github" / "workflows", @@ -57,6 +58,7 @@ def test_works_as_defined(tmp_path, test_project_config_factory): "merge-gate": {"exists": True, "secrets": ("SONAR_TOKEN",)}, }, "dependency_manager_version": "2.3.0", + "has_documentation": True, "minimum_python_version": "3.10", "os_version": "ubuntu-24.04", "python_versions": ( diff --git a/test/unit/nox/_workflow_test.py b/test/unit/nox/_workflow_test.py index fad8a1013..ac10d6ad9 100644 --- a/test/unit/nox/_workflow_test.py +++ b/test/unit/nox/_workflow_test.py @@ -10,7 +10,10 @@ check_workflow, generate_workflow, ) -from exasol.toolbox.util.workflows.templates import WORKFLOW_TEMPLATE_OPTIONS +from exasol.toolbox.util.workflows.templates import ( + DOCUMENTATION_ONLY_WORKFLOW_NAMES, + WORKFLOW_TEMPLATE_OPTIONS, +) from exasol.toolbox.util.workflows.workflow_orchestrator import ALL @@ -31,6 +34,20 @@ def github_workflow_patcher_yaml(self) -> None: ) +@pytest.fixture +def project_config_without_documentation(tmp_path) -> BaseConfig: + class Config(BaseConfig): + @computed_field # type: ignore[misc] + @property + def has_documentation(self) -> bool: + return False + + return Config( + root_path=tmp_path, + project_name="test", + ) + + @pytest.fixture def nox_session_runner_posargs(request): value = request.param @@ -83,6 +100,41 @@ def test_raises_exception_when_name_incorrect( assert "invalid choice: 'not-a-valid-name'" in capsys.readouterr().err + @staticmethod + @pytest.mark.parametrize( + "nox_session_runner_posargs, expected_count", + [(ALL, 13)], + indirect=["nox_session_runner_posargs"], + ) + def test_skips_documentation_workflows_when_docs_disabled( + nox_session, + project_config_without_documentation, + nox_session_runner_posargs, + expected_count, + ): + with patch( + "exasol.toolbox.nox._workflow.PROJECT_CONFIG", + new=project_config_without_documentation, + ): + generate_workflow(nox_session) + + count = sum( + 1 + for _ in project_config_without_documentation.github_workflow_directory.glob( + "*.yml" + ) + ) + assert count == expected_count + existing_workflows = { + workflow.name + for workflow in project_config_without_documentation.github_workflow_directory.glob( + "*.yml" + ) + } + assert {f"{name}.yml" for name in DOCUMENTATION_ONLY_WORKFLOW_NAMES}.isdisjoint( + existing_workflows + ) + class TestCheckWorkflow: @staticmethod @@ -147,6 +199,44 @@ def test_raises_session_quit_when_workflows_are_out_of_date( "- slow-checks" ) + @staticmethod + @pytest.mark.parametrize( + "nox_session_runner_posargs", + [ALL], + indirect=["nox_session_runner_posargs"], + ) + def test_raises_session_quit_without_documentation_workflows( + nox_session, + project_config_without_documentation, + nox_session_runner_posargs, + ): + with ( + patch("exasol.toolbox.util.workflows.workflow_orchestrator.logger.info"), + patch( + "exasol.toolbox.nox._workflow.PROJECT_CONFIG", + new=project_config_without_documentation, + ), + ): + with pytest.raises(_SessionQuit) as exc: + check_workflow(nox_session) + + assert str(exc.value) == ( + "\n13 workflows are out of date:\n" + "- build-and-publish\n" + "- cd\n" + "- check-release-tag\n" + "- checks\n" + "- ci\n" + "- dependency-update\n" + "- fast-tests\n" + "- gh-pages\n" + "- matrix\n" + "- merge-gate\n" + "- periodic-validation\n" + "- report\n" + "- slow-checks" + ) + class TestAuditWorkflows: @staticmethod diff --git a/test/unit/util/workflows/workflow_orchestrator_test.py b/test/unit/util/workflows/workflow_orchestrator_test.py index 640a15e28..63316dd5e 100644 --- a/test/unit/util/workflows/workflow_orchestrator_test.py +++ b/test/unit/util/workflows/workflow_orchestrator_test.py @@ -2,12 +2,15 @@ from unittest.mock import patch import pytest +from pydantic import computed_field +from exasol.toolbox.config import BaseConfig from exasol.toolbox.util.workflows.exceptions import ( InvalidWorkflowPatcherEntryError, YamlJobValueError, ) from exasol.toolbox.util.workflows.templates import ( + DOCUMENTATION_ONLY_WORKFLOW_NAMES, NOT_MAINTAINED_WORKFLOW_NAMES, WORKFLOW_TEMPLATE_OPTIONS, ) @@ -93,6 +96,45 @@ def test_returns_false_for_maintained_workflow(project_config): assert result is False + @staticmethod + @pytest.mark.parametrize("workflow_name", DOCUMENTATION_ONLY_WORKFLOW_NAMES) + def test_returns_ralse_for_documentation_only_workflow_when_docs_enabled( + project_config, workflow_name + ): + + result = WorkflowOrchestrator( + workflow_choice=workflow_name, + config=project_config, + )._skip_workflow( + workflow_name=workflow_name, + is_new_project=False, + ) + + assert result is False + + @staticmethod + @pytest.mark.parametrize("workflow_name", DOCUMENTATION_ONLY_WORKFLOW_NAMES) + def test_returns_true_for_documentation_only_workflow_when_docs_disabled( + tmp_path, workflow_name + ): + class Config(BaseConfig): + @computed_field # type: ignore[misc] + @property + def has_documentation(self) -> bool: + return False + + config = Config(root_path=tmp_path, project_name="test") + + result = WorkflowOrchestrator( + workflow_choice=workflow_name, + config=config, + )._skip_workflow( + workflow_name=workflow_name, + is_new_project=False, + ) + + assert result is True + class TestIterWorkflows: @staticmethod