diff --git a/.github/workflows/matrix-all.yml b/.github/workflows/matrix-all.yml deleted file mode 100644 index cc8e849b0..000000000 --- a/.github/workflows/matrix-all.yml +++ /dev/null @@ -1,41 +0,0 @@ -# Generated and maintained by the exasol-toolbox. -# Last generated with exasol-toolbox version 8.1.1. -name: Build Matrix (All Versions) - -on: - workflow_call: - outputs: - matrix: - description: "Generates the all versions build matrix" - value: ${{ jobs.set-matrix-all.outputs.matrix }} - -jobs: - set-matrix-all: - runs-on: "ubuntu-24.04" - permissions: - contents: read - steps: - - name: Check out Repository - id: check-out-repository - uses: actions/checkout@v6 - with: - persist-credentials: false - - - name: Set up Python & Poetry Environment - id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v8 - with: - python-version: "3.10" - poetry-version: "2.3.0" - - - name: Generate Matrix - id: generate-matrix - run: poetry run -- nox -s matrix:all - - - name: Set Matrix - id: set-matrix - run: | - echo "matrix=$(poetry run -- nox -s matrix:all)" >> $GITHUB_OUTPUT - - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} diff --git a/.github/workflows/matrix-exasol.yml b/.github/workflows/matrix-exasol.yml deleted file mode 100644 index bab0ffb02..000000000 --- a/.github/workflows/matrix-exasol.yml +++ /dev/null @@ -1,41 +0,0 @@ -# Generated and maintained by the exasol-toolbox. -# Last generated with exasol-toolbox version 8.1.1. -name: Build Matrix (Exasol) - -on: - workflow_call: - outputs: - matrix: - description: "Generates the exasol version build matrix" - value: ${{ jobs.set-matrix-exasol.outputs.matrix }} - -jobs: - set-matrix-exasol: - runs-on: "ubuntu-24.04" - permissions: - contents: read - steps: - - name: Check out Repository - id: check-out-repository - uses: actions/checkout@v6 - with: - persist-credentials: false - - - name: Set up Python & Poetry Environment - id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v8 - with: - python-version: "3.10" - poetry-version: "2.3.0" - - - name: Generate Matrix - id: generate-matrix - run: poetry run -- nox -s matrix:exasol - - - name: Set Matrix - id: set-matrix - run: | - echo "matrix=$(poetry run -- nox -s matrix:exasol)" >> $GITHUB_OUTPUT - - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} diff --git a/.github/workflows/matrix-python.yml b/.github/workflows/matrix-python.yml deleted file mode 100644 index c671c36f3..000000000 --- a/.github/workflows/matrix-python.yml +++ /dev/null @@ -1,41 +0,0 @@ -# Generated and maintained by the exasol-toolbox. -# Last generated with exasol-toolbox version 8.1.1. -name: Build Matrix (Python) - -on: - workflow_call: - outputs: - matrix: - description: "Generates the python version build matrix" - value: ${{ jobs.set-matrix-python.outputs.matrix }} - -jobs: - set-matrix-python: - runs-on: "ubuntu-24.04" - permissions: - contents: read - steps: - - name: Check out Repository - id: check-out-repository - uses: actions/checkout@v6 - with: - persist-credentials: false - - - name: Set up Python & Poetry Environment - id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v8 - with: - python-version: "3.10" - poetry-version: "2.3.0" - - - name: Generate Matrix - id: generate-matrix - run: poetry run -- nox -s matrix:python - - - name: Set Matrix - id: set-matrix - run: | - echo "matrix=$(poetry run -- nox -s matrix:python)" >> $GITHUB_OUTPUT - - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} diff --git a/.github/workflows/matrix.yml b/.github/workflows/matrix.yml new file mode 100644 index 000000000..0f73f1285 --- /dev/null +++ b/.github/workflows/matrix.yml @@ -0,0 +1,79 @@ +# Generated and maintained by the exasol-toolbox. +# Last generated with exasol-toolbox version 8.1.1. +name: Build Matrix + +on: + workflow_call: + inputs: + matrix_keys_json: + description: "JSON array of BaseConfig keys to include in the generated matrix output." + required: true + type: string + outputs: + matrix: + description: "Generates the requested build matrix" + value: ${{ jobs.set-matrix.outputs.matrix }} + +jobs: + set-matrix: + runs-on: "ubuntu-24.04" + permissions: + contents: read + steps: + - name: Check out Repository + id: check-out-repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up Python & Poetry Environment + id: set-up-python-and-poetry-environment + uses: exasol/python-toolbox/.github/actions/python-environment@v8 + with: + python-version: "3.10" + poetry-version: "2.3.0" + + - name: Parse Matrix Keys + id: parse-matrix-keys + env: + MATRIX_KEYS_JSON: ${{ inputs.matrix_keys_json }} + run: | + python - <<'PY' + import json + import os + + matrix_keys = json.loads(os.environ["MATRIX_KEYS_JSON"]) + with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output_file: + print(f"matrix_keys_json={json.dumps(matrix_keys)}", file=output_file) + PY + + - name: Generate Matrix + id: set-matrix + env: + MATRIX_KEYS_JSON: ${{ steps.parse-matrix-keys.outputs.matrix_keys_json }} + run: | + python - <<'PY' + import json + import os + import subprocess + + matrix_keys = json.loads(os.environ["MATRIX_KEYS_JSON"]) + matrix_json = subprocess.check_output( + [ + "poetry", + "run", + "--", + "nox", + "-s", + "matrix:generate", + "--", + *matrix_keys, + ], + text=True, + ).strip() + with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output_file: + print(f"matrix={matrix_json}", file=output_file) + PY + + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} diff --git a/.github/workflows/slow-checks.yml b/.github/workflows/slow-checks.yml index 8c4cd585f..5e127d1cd 100644 --- a/.github/workflows/slow-checks.yml +++ b/.github/workflows/slow-checks.yml @@ -6,12 +6,14 @@ on: jobs: build-matrix: name: Build Matrix - uses: ./.github/workflows/matrix-python.yml + uses: ./.github/workflows/matrix.yml + with: + matrix_keys_json: '["python_versions"]' permissions: contents: read run-integration-tests: - name: Run Integration Tests (Python-${{ matrix.python-version }}) + name: Run Integration Tests (Python-${{ matrix.python_versions }}) needs: - build-matrix runs-on: "ubuntu-24.04" @@ -31,7 +33,7 @@ jobs: id: set-up-python-and-poetry-environment uses: exasol/python-toolbox/.github/actions/python-environment@v8 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ matrix.python_versions }} poetry-version: "2.3.0" - name: Run Integration Tests @@ -42,7 +44,7 @@ jobs: id: upload-artifacts uses: actions/upload-artifact@v7 with: - name: coverage-python${{ matrix.python-version }}-slow + name: coverage-python${{ matrix.python_versions }}-slow path: .coverage include-hidden-files: true overwrite: false diff --git a/.github/workflows/test-python-environment.yml b/.github/workflows/test-python-environment.yml index f05267e9a..10432180e 100644 --- a/.github/workflows/test-python-environment.yml +++ b/.github/workflows/test-python-environment.yml @@ -41,7 +41,9 @@ jobs: needs: - check-changes if: needs.check-changes.outputs.should_run == 'true' - uses: ./.github/workflows/matrix-all.yml + uses: ./.github/workflows/matrix.yml + with: + matrix_keys_json: '["python_versions"]' permissions: contents: read @@ -60,8 +62,8 @@ jobs: - int-linux-x64-4core-gpu-t4-ubuntu24.04-1 - int-linux-x64-4core-ubuntu24.04-1 - int-linux-x64-2core-ubuntu24.04-1 - python-version: ${{ fromJson(needs.build-matrix.outputs.matrix).python-version }} - name: Verify Poetry Setup for ${{ matrix.runner }} (Python-${{ matrix.python-version }}) + python-versions: ${{ fromJson(needs.build-matrix.outputs.matrix).python_versions }} + name: Verify Poetry Setup for ${{ matrix.runner }} (Python-${{ matrix.python-versions }}) runs-on: labels: ${{ matrix.runner }} steps: @@ -75,7 +77,7 @@ jobs: id: set-up-python-and-poetry-environment uses: ./.github/actions/python-environment with: - python-version: "${{ matrix.python-version }}" + python-version: "${{ matrix.python-versions }}" poetry-version: "2.3.0" - name: Check Poetry Version @@ -85,7 +87,7 @@ jobs: - name: Validate Python Version id: validate-python-version env: - PYTHON_VERSION: ${{ matrix.python-version }} + PYTHON_VERSION: ${{ matrix.python-versions }} run: | poetry run which python poetry run python --version diff --git a/doc/user_guide/features/github_workflows/index.rst b/doc/user_guide/features/github_workflows/index.rst index 0e699d9a8..0b550ec93 100644 --- a/doc/user_guide/features/github_workflows/index.rst +++ b/doc/user_guide/features/github_workflows/index.rst @@ -84,6 +84,10 @@ Maintained by the PTB - Workflow call - Calls Nox session ``matrix:all``, which typically evaluates ``exasol_versions`` and ``python_versions`` from the ``PROJECT_CONFIG``. + * - ``matrix.yml`` + - Workflow call + - Calls Nox session ``matrix:generate`` with one or more space-separated + ``BaseConfig`` keys to build a custom matrix from the ``PROJECT_CONFIG``. * - ``matrix-exasol.yml`` - Workflow call - Calls Nox session ``matrix:exasol`` to get the ``exasol_versions`` from the diff --git a/exasol/toolbox/nox/_ci.py b/exasol/toolbox/nox/_ci.py index ff3f047dc..af9bfc6fe 100644 --- a/exasol/toolbox/nox/_ci.py +++ b/exasol/toolbox/nox/_ci.py @@ -1,5 +1,7 @@ +import argparse import json -import logging +from collections.abc import Iterable +from typing import Any import nox from nox import Session @@ -9,32 +11,121 @@ PROJECT_CONFIG, ) -_log = logging.getLogger(__name__) +# The relevant nox sessions will be removed in: +# https://github.com/exasol/python-toolbox/issues/859 +MATRIX_DEPRECATION_DATE = "2026-09-15" -def _python_matrix(config: BaseConfig): - return {"python-version": config.python_versions} +def _matrix_keys(config: BaseConfig) -> tuple[str, ...]: + """ + Return the config keys that are valid for matrix generation. + Includes both declared fields and computed fields. + """ -def _exasol_matrix(config: BaseConfig): - return {"exasol-version": config.exasol_versions} + config_class = type(config) + return tuple(config_class.model_fields) + tuple(config_class.model_computed_fields) + + +def _matrix_args(session: Session, config: BaseConfig) -> list[str]: + parser = argparse.ArgumentParser( + prog="nox -s matrix:generate", + usage="nox -s matrix:generate -- [ ...]", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument( + "keys", + nargs="+", + choices=sorted(_matrix_keys(config)), + help="BaseConfig keys to include in the generated matrix output", + ) + return parser.parse_args(session.posargs).keys + + +def _dump_matrix(config: BaseConfig, keys: Iterable[str]) -> dict[str, Any]: + """ + Build a JSON-serializable matrix subset from the project's config. + + GitHub Actions matrix values must be arrays. Pydantic already serializes + tuple-based config values to lists, so scalar values are wrapped in a + single-element list here. + """ + + allowed_keys = set(_matrix_keys(config)) + requested_keys = tuple(keys) + for key in requested_keys: + if key not in allowed_keys: + raise KeyError(key) + + matrix = config.model_dump(mode="json", include=set(requested_keys)) + for key, value in matrix.items(): + if not isinstance(value, list): + matrix[key] = [value] + return matrix + + +def _print_deprecated_matrix( + session: Session, + config: BaseConfig, + key_map: dict[str, str], + session_name: str, + replacement_args: str, +) -> None: + matrix = _dump_matrix(config, key_map.values()) + renamed_matrix: dict[str, Any] = {} + for output_key, config_key in key_map.items(): + renamed_matrix[output_key] = matrix[config_key] + print(json.dumps(renamed_matrix)) + + session.warn( + f"Warning: `nox -s {session_name}` is deprecated and will be removed on " + f"{MATRIX_DEPRECATION_DATE}. Use `nox -s matrix:generate -- {replacement_args}` " + "instead." + ) + + +@nox.session(name="matrix:generate", python=False) +def generate_matrix(session: Session) -> None: + """Output selected BaseConfig values as JSON.""" + keys = _matrix_args(session, PROJECT_CONFIG) + matrix = _dump_matrix(PROJECT_CONFIG, keys) + print(json.dumps(matrix)) @nox.session(name="matrix:python", python=False) def python_matrix(session: Session) -> None: """Output the build matrix for Python versions as JSON.""" - print(json.dumps(_python_matrix(PROJECT_CONFIG))) + _print_deprecated_matrix( + session=session, + config=PROJECT_CONFIG, + key_map={"python-version": "python_versions"}, + session_name="matrix:python", + replacement_args="python_versions", + ) @nox.session(name="matrix:exasol", python=False) def exasol_matrix(session: Session) -> None: """Output the build matrix for Exasol versions as JSON.""" - print(json.dumps(_exasol_matrix(PROJECT_CONFIG))) + _print_deprecated_matrix( + session=session, + config=PROJECT_CONFIG, + key_map={"exasol-version": "exasol_versions"}, + session_name="matrix:exasol", + replacement_args="exasol_versions", + ) @nox.session(name="matrix:all", python=False) def full_matrix(session: Session) -> None: """Output the full build matrix for Python & Exasol versions as JSON.""" - matrix = _python_matrix(PROJECT_CONFIG) - matrix.update(_exasol_matrix(PROJECT_CONFIG)) - print(json.dumps(matrix)) + _print_deprecated_matrix( + session=session, + config=PROJECT_CONFIG, + key_map={ + "python-version": "python_versions", + "exasol-version": "exasol_versions", + }, + session_name="matrix:all", + replacement_args="python_versions exasol_versions", + ) diff --git a/exasol/toolbox/nox/tasks.py b/exasol/toolbox/nox/tasks.py index 0fe726fbf..434417020 100644 --- a/exasol/toolbox/nox/tasks.py +++ b/exasol/toolbox/nox/tasks.py @@ -66,6 +66,7 @@ def check(session: Session) -> None: ) from exasol.toolbox.nox._ci import ( + generate_matrix, python_matrix, exasol_matrix, full_matrix, diff --git a/exasol/toolbox/templates/github/workflows/matrix-all.yml b/exasol/toolbox/templates/github/workflows/matrix-all.yml deleted file mode 100644 index c24c2f2db..000000000 --- a/exasol/toolbox/templates/github/workflows/matrix-all.yml +++ /dev/null @@ -1,40 +0,0 @@ -(( workflow_header )) -name: Build Matrix (All Versions) - -on: - workflow_call: - outputs: - matrix: - description: "Generates the all versions build matrix" - value: ${{ jobs.set-matrix-all.outputs.matrix }} - -jobs: - set-matrix-all: - runs-on: "(( os_version ))" - permissions: - contents: read - steps: - - name: Check out Repository - id: check-out-repository - uses: actions/checkout@v6 - with: - persist-credentials: false - - - name: Set up Python & Poetry Environment - id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v8 - with: - python-version: "(( minimum_python_version ))" - poetry-version: "(( dependency_manager_version ))" - - - name: Generate Matrix - id: generate-matrix - run: poetry run -- nox -s matrix:all - - - name: Set Matrix - id: set-matrix - run: | - echo "matrix=$(poetry run -- nox -s matrix:all)" >> $GITHUB_OUTPUT - - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} diff --git a/exasol/toolbox/templates/github/workflows/matrix-exasol.yml b/exasol/toolbox/templates/github/workflows/matrix-exasol.yml deleted file mode 100644 index 18b3b851b..000000000 --- a/exasol/toolbox/templates/github/workflows/matrix-exasol.yml +++ /dev/null @@ -1,40 +0,0 @@ -(( workflow_header )) -name: Build Matrix (Exasol) - -on: - workflow_call: - outputs: - matrix: - description: "Generates the exasol version build matrix" - value: ${{ jobs.set-matrix-exasol.outputs.matrix }} - -jobs: - set-matrix-exasol: - runs-on: "(( os_version ))" - permissions: - contents: read - steps: - - name: Check out Repository - id: check-out-repository - uses: actions/checkout@v6 - with: - persist-credentials: false - - - name: Set up Python & Poetry Environment - id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v8 - with: - python-version: "(( minimum_python_version ))" - poetry-version: "(( dependency_manager_version ))" - - - name: Generate Matrix - id: generate-matrix - run: poetry run -- nox -s matrix:exasol - - - name: Set Matrix - id: set-matrix - run: | - echo "matrix=$(poetry run -- nox -s matrix:exasol)" >> $GITHUB_OUTPUT - - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} diff --git a/exasol/toolbox/templates/github/workflows/matrix-python.yml b/exasol/toolbox/templates/github/workflows/matrix-python.yml deleted file mode 100644 index 062426ff1..000000000 --- a/exasol/toolbox/templates/github/workflows/matrix-python.yml +++ /dev/null @@ -1,40 +0,0 @@ -(( workflow_header )) -name: Build Matrix (Python) - -on: - workflow_call: - outputs: - matrix: - description: "Generates the python version build matrix" - value: ${{ jobs.set-matrix-python.outputs.matrix }} - -jobs: - set-matrix-python: - runs-on: "(( os_version ))" - permissions: - contents: read - steps: - - name: Check out Repository - id: check-out-repository - uses: actions/checkout@v6 - with: - persist-credentials: false - - - name: Set up Python & Poetry Environment - id: set-up-python-and-poetry-environment - uses: exasol/python-toolbox/.github/actions/python-environment@v8 - with: - python-version: "(( minimum_python_version ))" - poetry-version: "(( dependency_manager_version ))" - - - name: Generate Matrix - id: generate-matrix - run: poetry run -- nox -s matrix:python - - - name: Set Matrix - id: set-matrix - run: | - echo "matrix=$(poetry run -- nox -s matrix:python)" >> $GITHUB_OUTPUT - - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} diff --git a/exasol/toolbox/templates/github/workflows/matrix.yml b/exasol/toolbox/templates/github/workflows/matrix.yml new file mode 100644 index 000000000..e4b6ec331 --- /dev/null +++ b/exasol/toolbox/templates/github/workflows/matrix.yml @@ -0,0 +1,78 @@ +(( workflow_header )) +name: Build Matrix + +on: + workflow_call: + inputs: + matrix_keys_json: + description: "JSON array of BaseConfig keys to include in the generated matrix output." + required: true + type: string + outputs: + matrix: + description: "Generates the requested build matrix" + value: ${{ jobs.set-matrix.outputs.matrix }} + +jobs: + set-matrix: + runs-on: "(( os_version ))" + permissions: + contents: read + steps: + - name: Check out Repository + id: check-out-repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up Python & Poetry Environment + id: set-up-python-and-poetry-environment + uses: exasol/python-toolbox/.github/actions/python-environment@v8 + with: + python-version: "(( minimum_python_version ))" + poetry-version: "(( dependency_manager_version ))" + + - name: Parse Matrix Keys + id: parse-matrix-keys + env: + MATRIX_KEYS_JSON: ${{ inputs.matrix_keys_json }} + run: | + python - <<'PY' + import json + import os + + matrix_keys = json.loads(os.environ["MATRIX_KEYS_JSON"]) + with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output_file: + print(f"matrix_keys_json={json.dumps(matrix_keys)}", file=output_file) + PY + + - name: Generate Matrix + id: set-matrix + env: + MATRIX_KEYS_JSON: ${{ steps.parse-matrix-keys.outputs.matrix_keys_json }} + run: | + python - <<'PY' + import json + import os + import subprocess + + matrix_keys = json.loads(os.environ["MATRIX_KEYS_JSON"]) + matrix_json = subprocess.check_output( + [ + "poetry", + "run", + "--", + "nox", + "-s", + "matrix:generate", + "--", + *matrix_keys, + ], + text=True, + ).strip() + with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output_file: + print(f"matrix={matrix_json}", file=output_file) + PY + + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} diff --git a/exasol/toolbox/templates/github/workflows/slow-checks.yml b/exasol/toolbox/templates/github/workflows/slow-checks.yml index 618e1790a..01b9a6c0c 100644 --- a/exasol/toolbox/templates/github/workflows/slow-checks.yml +++ b/exasol/toolbox/templates/github/workflows/slow-checks.yml @@ -8,12 +8,14 @@ on: jobs: build-matrix: name: Build Matrix - uses: ./.github/workflows/matrix-all.yml + uses: ./.github/workflows/matrix.yml + with: + matrix_keys_json: '["python_versions","exasol_versions"]' permissions: contents: read run-integration-tests: - name: Run Integration Tests (Python-${{ matrix.python-version }}, Exasol-${{ matrix.exasol-version}}) + name: Run Integration Tests (Python-${{ matrix.python_versions }}, Exasol-${{ matrix.exasol_versions}}) needs: - build-matrix runs-on: "(( os_version ))" @@ -35,18 +37,18 @@ jobs: id: set-up-python-and-poetry-environment uses: exasol/python-toolbox/.github/actions/python-environment@v8 with: - python-version: ${{ matrix.python-version }} + python-version: ${{ matrix.python_versions }} poetry-version: "(( dependency_manager_version ))" - name: Run Integration Tests id: run-integration-tests - run: poetry run -- nox -s test:integration -- --coverage --db-version ${{ matrix.exasol-version }} + run: poetry run -- nox -s test:integration -- --coverage --db-version ${{ matrix.exasol_versions }} - name: Upload Artifacts id: upload-artifacts uses: actions/upload-artifact@v7 with: - name: coverage-python${{ matrix.python-version }}-exasol${{ matrix.exasol-version }}-slow + name: coverage-python${{ matrix.python_versions }}-exasol${{ matrix.exasol_versions }}-slow path: .coverage include-hidden-files: true overwrite: false diff --git a/project-template/{{cookiecutter.repo_name}}/pyproject.toml b/project-template/{{cookiecutter.repo_name}}/pyproject.toml index db6d6ca7d..cdf34b5ef 100644 --- a/project-template/{{cookiecutter.repo_name}}/pyproject.toml +++ b/project-template/{{cookiecutter.repo_name}}/pyproject.toml @@ -35,7 +35,7 @@ include = [ [tool.poetry.requires-plugins] poetry-plugin-export = ">=1.8" -[poetry.urls] +[project.urls] repository = "https://github.com/exasol/{{cookiecutter.repo_name}}" homepage = "https://github.com/exasol/{{cookiecutter.repo_name}}" diff --git a/pyproject.toml b/pyproject.toml index c824c1faf..178c9b8b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,7 +84,7 @@ include = [ "exasol/toolbox/templates/**/*" ] -[poetry.urls] +[project.urls] Homepage = "https://www.exasol.com/" Documentation = "https://exasol.github.io/python-toolbox/" Source = "https://github.com/exasol/python-toolbox" diff --git a/test/integration/project-template/nox_test.py b/test/integration/project-template/nox_test.py index e06fe9f62..994e998fa 100644 --- a/test/integration/project-template/nox_test.py +++ b/test/integration/project-template/nox_test.py @@ -83,4 +83,4 @@ def test_install_github_workflows(self, poetry_path, run_command): assert output.returncode == 0 file_list = run_command(["ls", ".github/workflows"]).stdout.splitlines() - assert len(file_list) == 16 + assert len(file_list) == 14 diff --git a/test/unit/nox/_ci_test.py b/test/unit/nox/_ci_test.py new file mode 100644 index 000000000..7fc0adb08 --- /dev/null +++ b/test/unit/nox/_ci_test.py @@ -0,0 +1,141 @@ +from __future__ import annotations + +import json +from unittest.mock import patch + +import pytest +from pydantic import computed_field + +from exasol.toolbox.config import BaseConfig +from exasol.toolbox.nox._ci import ( + _dump_matrix, + exasol_matrix, + full_matrix, + generate_matrix, + python_matrix, +) + + +@pytest.fixture +def nox_session_runner_posargs(request) -> list[str]: + return list(getattr(request, "param", [])) + + +@pytest.fixture +def config(tmp_path) -> BaseConfig: + class Config(BaseConfig): + extra_matrix_value: str = "extra" + + @computed_field # type: ignore[misc] + @property + def computed_matrix_value(self) -> str: + return f"{self.project_name}-computed" + + return Config(root_path=tmp_path, project_name="toolbox") + + +class TestGenerateMatrix: + @staticmethod + @pytest.mark.parametrize( + "nox_session_runner_posargs", + [["computed_matrix_value", "extra_matrix_value"]], + indirect=True, + ) + def test_uses_requested_field_names( + nox_session, + config, + capsys, + nox_session_runner_posargs, + ): + with patch("exasol.toolbox.nox._ci.PROJECT_CONFIG", new=config): + generate_matrix(nox_session) + + assert json.loads(capsys.readouterr().out) == { + "computed_matrix_value": ["toolbox-computed"], + "extra_matrix_value": ["extra"], + } + + @staticmethod + @pytest.mark.parametrize( + "nox_session_runner_posargs", + [["missing_value"]], + indirect=True, + ) + def test_rejects_unknown_field( + nox_session, + config, + capsys, + nox_session_runner_posargs, + ): + with patch("exasol.toolbox.nox._ci.PROJECT_CONFIG", new=config): + with pytest.raises(SystemExit): + generate_matrix(nox_session) + + assert "invalid choice: 'missing_value'" in capsys.readouterr().err + + +class TestDumpMatrix: + @staticmethod + @pytest.mark.parametrize( + ("requested_keys", "expected"), + [ + ( + ("computed_matrix_value",), + {"computed_matrix_value": ["toolbox-computed"]}, + ), + ( + ("computed_matrix_value", "extra_matrix_value"), + { + "computed_matrix_value": ["toolbox-computed"], + "extra_matrix_value": ["extra"], + }, + ), + ], + ) + def test_returns_requested_keys(config, requested_keys, expected): + assert _dump_matrix(config, requested_keys) == expected + + @staticmethod + def test_rejects_unknown_key(config): + with pytest.raises(KeyError, match="missing_matrix_value"): + _dump_matrix(config, ("missing_matrix_value",)) + + +class TestDeprecatedMatrixSessions: + @staticmethod + def test_exasol_session_still_emits_field_names( + nox_session, config, caplog, capsys + ): + with patch("exasol.toolbox.nox._ci.PROJECT_CONFIG", new=config): + exasol_matrix(nox_session) + + captured = capsys.readouterr() + assert json.loads(captured.out) == { + "exasol-version": ["7.1.30", "8.29.13", "2025.1.8"] + } + assert len(caplog.messages) == 1 + + @staticmethod + def test_python_session_still_emits_field_names( + nox_session, config, caplog, capsys + ): + with patch("exasol.toolbox.nox._ci.PROJECT_CONFIG", new=config): + python_matrix(nox_session) + + captured = capsys.readouterr() + assert json.loads(captured.out) == { + "python-version": ["3.10", "3.11", "3.12", "3.13", "3.14"] + } + assert len(caplog.messages) == 1 + + @staticmethod + def test_full_session_still_emits_field_names(nox_session, config, caplog, capsys): + with patch("exasol.toolbox.nox._ci.PROJECT_CONFIG", new=config): + full_matrix(nox_session) + + captured = capsys.readouterr() + assert json.loads(captured.out) == { + "python-version": ["3.10", "3.11", "3.12", "3.13", "3.14"], + "exasol-version": ["7.1.30", "8.29.13", "2025.1.8"], + } + assert len(caplog.messages) == 1 diff --git a/test/unit/nox/_workflow_test.py b/test/unit/nox/_workflow_test.py index 983d16256..55ec74c10 100644 --- a/test/unit/nox/_workflow_test.py +++ b/test/unit/nox/_workflow_test.py @@ -39,7 +39,7 @@ class TestGenerateWorkflow: @staticmethod @pytest.mark.parametrize( "nox_session_runner_posargs, expected_count", - [(ALL, 16), *[(key, 1) for key in WORKFLOW_TEMPLATE_OPTIONS.keys()]], + [(ALL, 14), *[(key, 1) for key in WORKFLOW_TEMPLATE_OPTIONS.keys()]], indirect=["nox_session_runner_posargs"], ) def test_works_as_expected( @@ -128,7 +128,7 @@ def test_raises_session_quit_when_workflows_are_out_of_date( check_workflow(nox_session) assert str(exc.value) == ( - "\n16 workflows are out of date:\n" + "\n14 workflows are out of date:\n" "- build-and-publish\n" "- cd\n" "- check-release-tag\n" @@ -137,9 +137,7 @@ def test_raises_session_quit_when_workflows_are_out_of_date( "- dependency-update\n" "- fast-tests\n" "- gh-pages\n" - "- matrix-all\n" - "- matrix-exasol\n" - "- matrix-python\n" + "- matrix\n" "- merge-gate\n" "- periodic-validation\n" "- pr-merge\n" diff --git a/test/unit/util/workflows/templates_test.py b/test/unit/util/workflows/templates_test.py index 32a62957a..8969ad658 100644 --- a/test/unit/util/workflows/templates_test.py +++ b/test/unit/util/workflows/templates_test.py @@ -25,9 +25,7 @@ def test_get_workflow_templates(project_config): "fast-tests", "dependency-update", "gh-pages", - "matrix-all", - "matrix-exasol", - "matrix-python", + "matrix", "merge-gate", "periodic-validation", "pr-merge",