From 6b7ed774b86c9e73bd69c4d30895d9ee0e36d395 Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Wed, 29 Jan 2025 14:58:40 -0800 Subject: [PATCH 1/3] /* PR_START p--release-dbt-metricflow-0.8.1-updates 04 */ For package tests, activate python venv via shell to handle PATH more easily. --- scripts/ci_tests/run_package_build_tests.py | 57 +++++++++++++++------ 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/scripts/ci_tests/run_package_build_tests.py b/scripts/ci_tests/run_package_build_tests.py index 349b509107..5abcc65e1a 100644 --- a/scripts/ci_tests/run_package_build_tests.py +++ b/scripts/ci_tests/run_package_build_tests.py @@ -8,6 +8,7 @@ import logging import tempfile import venv +from collections.abc import Sequence from pathlib import Path from scripts.mf_script_helper import MetricFlowScriptHelper @@ -15,7 +16,9 @@ logger = logging.getLogger(__name__) -def _run_package_build_test(package_directory: Path, package_test_script: Path) -> None: +def _run_package_build_test( + package_directory: Path, package_test_script: Path, optional_package_dependencies_to_install: Sequence[str] = () +) -> None: """Run a test to verify that a package is built properly. Given the directory where the package is located, this will build the package using `hatch build` and install the @@ -25,40 +28,62 @@ def _run_package_build_test(package_directory: Path, package_test_script: Path) Args: package_directory: Root directory where the package is located. package_test_script: The path to the script that should be run. - + optional_package_dependencies_to_install: If the given package defines optional dependencies that can be + installed, install these. e.g. for `dbt-metricflow[dbt-duckdb]`, specify `dbt-duckdb`. Returns: None Raises: Exception on test failure. """ - logger.info(f"Running package build test for {str(package_directory)!r} using {str(package_test_script)!r}") + package_directory_str = package_directory.as_posix() + package_test_script_str = package_test_script.as_posix() + logger.info(f"Running package build test for {package_directory_str!r} using {package_test_script_str!r}") + try: with tempfile.TemporaryDirectory() as temporary_directory_str: temporary_directory = Path(temporary_directory_str) venv_directory = temporary_directory.joinpath("venv") - logger.info(f"Creating venv at {str(venv_directory)!r}") + logger.info(f"Creating a new venv at {venv_directory.as_posix()!r}") venv.create(venv_directory, with_pip=True) - pip_executable = Path(venv_directory, "bin/pip") - python_executable = Path(venv_directory, "bin/python") + pip_executable = Path(venv_directory, "bin/pip").as_posix() - logger.info(f"Building package at {str(package_directory)!r}") - logger.info(f"Running package build test for {str(package_directory)!r} using {str(package_test_script)!r}") + logger.info(f"Building package at {package_directory_str!r}") + MetricFlowScriptHelper.run_command(["hatch", "clean"], working_directory=package_directory) MetricFlowScriptHelper.run_command(["hatch", "build"], working_directory=package_directory) - logger.info("Installing package using generated wheels") - MetricFlowScriptHelper.run_shell_command(f'{pip_executable} install "{str(package_directory)}"/dist/*.whl') - - logger.info("Running test using installed package in venv") - MetricFlowScriptHelper.run_command( - [str(python_executable), str(package_test_script)], working_directory=temporary_directory + logger.info("Installing package in venv using generated wheels") + paths_to_wheels = _get_wheels_in_directory(package_directory.joinpath("dist")) + if len(paths_to_wheels) != 1: + raise RuntimeError(f"Expected exactly one wheel but got {paths_to_wheels}") + + path_to_wheel = paths_to_wheels[0] + MetricFlowScriptHelper.run_command([pip_executable, "install", path_to_wheel.as_posix()]) + for optional_package_dependency in optional_package_dependencies_to_install: + MetricFlowScriptHelper.run_command( + [pip_executable, "install", f"{path_to_wheel.as_posix()}[{optional_package_dependency}]"] + ) + + logger.info("Running test using venv") + venv_activate = venv_directory.joinpath("bin", "activate").as_posix() + MetricFlowScriptHelper.run_shell_command( + # Using period instead of `source` for compatibility with `sh`. + f"cd {temporary_directory_str} && . {venv_activate} && python {package_test_script_str}", ) - logger.info(f"Test passed {str(package_test_script)!r}") + logger.info(f"Test passed {package_test_script_str!r}") except Exception as e: raise PackageBuildTestFailureException( - f"Package build test failed for {str(package_directory)!r} using {str(package_test_script)!r}" + f"Package build test failed for {package_directory_str!r} using {package_test_script_str!r}" ) from e +def _get_wheels_in_directory(directory: Path) -> Sequence[Path]: + paths_to_wheels = [] + for path_item in directory.iterdir(): + if path_item.is_file() and path_item.suffix == ".whl": + paths_to_wheels.append(path_item) + return paths_to_wheels + + class PackageBuildTestFailureException(Exception): # noqa: D101 pass From 7fd998be9eb946d66dbeba8df9e0cbc7501d0180 Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Wed, 29 Jan 2025 14:55:19 -0800 Subject: [PATCH 2/3] Add package test for `dbt-metricflow` --- .../workflows/ci-metricflow-unit-tests.yaml | 1 + .../ci_tests/dbt_metricflow_package_test.py | 21 +++++++++++++++++++ scripts/ci_tests/run_package_build_tests.py | 9 ++++++-- 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 scripts/ci_tests/dbt_metricflow_package_test.py diff --git a/.github/workflows/ci-metricflow-unit-tests.yaml b/.github/workflows/ci-metricflow-unit-tests.yaml index cda67f00bc..b9b46be2e8 100644 --- a/.github/workflows/ci-metricflow-unit-tests.yaml +++ b/.github/workflows/ci-metricflow-unit-tests.yaml @@ -93,4 +93,5 @@ jobs: } - name: Run Package-Build Tests + shell: bash run: "make test-build-packages" diff --git a/scripts/ci_tests/dbt_metricflow_package_test.py b/scripts/ci_tests/dbt_metricflow_package_test.py new file mode 100644 index 0000000000..2f56d92f9b --- /dev/null +++ b/scripts/ci_tests/dbt_metricflow_package_test.py @@ -0,0 +1,21 @@ +from __future__ import annotations + +import subprocess +from pathlib import Path + +if __name__ == "__main__": + # Check that the `mf` command is installed. + print(f"Running from {Path.cwd().as_posix()}") + print("Checking path to python") + subprocess.check_call("which python", shell=True) + subprocess.check_call("mf", shell=True) + # Run the tutorial using `--yes` to create the sample project without user interaction. + subprocess.check_call("mf tutorial --yes", shell=True) + tutorial_directory = Path.cwd().joinpath("mf_tutorial_project").as_posix() + + # Run the first few tutorial steps. + subprocess.check_call("dbt seed", cwd=tutorial_directory, shell=True) + subprocess.check_call("dbt build", cwd=tutorial_directory, shell=True) + subprocess.check_call( + "mf query --metrics transactions --group-by metric_time --order metric_time", cwd=tutorial_directory, shell=True + ) diff --git a/scripts/ci_tests/run_package_build_tests.py b/scripts/ci_tests/run_package_build_tests.py index 5abcc65e1a..2135d4d726 100644 --- a/scripts/ci_tests/run_package_build_tests.py +++ b/scripts/ci_tests/run_package_build_tests.py @@ -105,10 +105,15 @@ class PackageBuildTestFailureException(Exception): # noqa: D101 package_test_script=metricflow_repo_directory.joinpath("scripts/ci_tests/metricflow_package_test.py"), ) - # Test building `metricflow-semantics` package. + # Test building the `metricflow-semantics` package. _run_package_build_test( package_directory=metricflow_repo_directory.joinpath("metricflow-semantics"), package_test_script=metricflow_repo_directory.joinpath("scripts/ci_tests/metricflow_semantics_package_test.py"), ) - # Add entry for `dbt-metricflow` once build issues are resolved. + # Test building the `dbt-metricflow` package. + _run_package_build_test( + package_directory=metricflow_repo_directory.joinpath("dbt-metricflow"), + package_test_script=metricflow_repo_directory.joinpath("scripts/ci_tests/dbt_metricflow_package_test.py"), + optional_package_dependencies_to_install=("dbt-duckdb",), + ) From fe348c7a8cd33b9cfc351e89b8bd9048f84f9e27 Mon Sep 17 00:00:00 2001 From: Paul Yang Date: Thu, 30 Jan 2025 10:23:42 -0800 Subject: [PATCH 3/3] Add print for shell commands run in test. --- .../ci_tests/dbt_metricflow_package_test.py | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/scripts/ci_tests/dbt_metricflow_package_test.py b/scripts/ci_tests/dbt_metricflow_package_test.py index 2f56d92f9b..caed49c220 100644 --- a/scripts/ci_tests/dbt_metricflow_package_test.py +++ b/scripts/ci_tests/dbt_metricflow_package_test.py @@ -1,21 +1,40 @@ from __future__ import annotations import subprocess +import textwrap from pathlib import Path +from typing import Optional + + +def _run_shell_command(command: str, cwd: Optional[Path] = None) -> None: + if cwd is None: + cwd = Path.cwd() + + print( + textwrap.dedent( + f"""\ + Running via shell: + command: {command!r} + cwd: {cwd.as_posix()!r} + """ + ).rstrip() + ) + subprocess.check_call(command, shell=True, cwd=cwd.as_posix()) + if __name__ == "__main__": # Check that the `mf` command is installed. - print(f"Running from {Path.cwd().as_posix()}") - print("Checking path to python") - subprocess.check_call("which python", shell=True) - subprocess.check_call("mf", shell=True) + _run_shell_command("which python") + _run_shell_command("which mf") + _run_shell_command("mf") # Run the tutorial using `--yes` to create the sample project without user interaction. - subprocess.check_call("mf tutorial --yes", shell=True) - tutorial_directory = Path.cwd().joinpath("mf_tutorial_project").as_posix() + _run_shell_command("mf tutorial --yes") + tutorial_directory = Path.cwd().joinpath("mf_tutorial_project") # Run the first few tutorial steps. - subprocess.check_call("dbt seed", cwd=tutorial_directory, shell=True) - subprocess.check_call("dbt build", cwd=tutorial_directory, shell=True) - subprocess.check_call( - "mf query --metrics transactions --group-by metric_time --order metric_time", cwd=tutorial_directory, shell=True + _run_shell_command("dbt seed", cwd=tutorial_directory) + _run_shell_command("dbt build", cwd=tutorial_directory) + _run_shell_command( + "mf query --metrics transactions --group-by metric_time --order metric_time", + cwd=tutorial_directory, )