diff --git a/.changes/unreleased/Breaking Changes-20231206-192442.yaml b/.changes/unreleased/Breaking Changes-20231206-192442.yaml new file mode 100644 index 00000000000..c6aad99bce5 --- /dev/null +++ b/.changes/unreleased/Breaking Changes-20231206-192442.yaml @@ -0,0 +1,6 @@ +kind: Breaking Changes +body: Fix changing the current working directory when using dpt deps, clean and init. +time: 2023-12-06T19:24:42.575372+09:00 +custom: + Author: rariyama + Issue: "8997" diff --git a/core/dbt/cli/main.py b/core/dbt/cli/main.py index ce0a42aae2e..9bb6e04c69e 100644 --- a/core/dbt/cli/main.py +++ b/core/dbt/cli/main.py @@ -218,10 +218,9 @@ def clean(ctx, **kwargs): """Delete all folders in the clean-targets list (usually the dbt_packages and target directories.)""" from dbt.task.clean import CleanTask - task = CleanTask(ctx.obj["flags"], ctx.obj["project"]) - - results = task.run() - success = task.interpret_results(results) + with CleanTask(ctx.obj["flags"], ctx.obj["project"]) as task: + results = task.run() + success = task.interpret_results(results) return results, success @@ -437,9 +436,9 @@ def deps(ctx, **kwargs): message=f"Version is required in --add-package when a package when source is {flags.SOURCE}", option_name="--add-package", ) - task = DepsTask(flags, ctx.obj["project"]) - results = task.run() - success = task.interpret_results(results) + with DepsTask(flags, ctx.obj["project"]) as task: + results = task.run() + success = task.interpret_results(results) return results, success @@ -459,10 +458,9 @@ def init(ctx, **kwargs): """Initialize a new dbt project.""" from dbt.task.init import InitTask - task = InitTask(ctx.obj["flags"]) - - results = task.run() - success = task.interpret_results(results) + with InitTask(ctx.obj["flags"]) as task: + results = task.run() + success = task.interpret_results(results) return results, success diff --git a/core/dbt/context/providers.py b/core/dbt/context/providers.py index 26f76edb218..d9010a9f4f9 100644 --- a/core/dbt/context/providers.py +++ b/core/dbt/context/providers.py @@ -975,7 +975,7 @@ def load_agate_table(self) -> "agate.Table": except ValueError as e: raise LoadAgateTableValueError(e, node=self.model) # this is used by some adapters - table.original_abspath = os.path.abspath(path) + table.original_abspath = os.path.abspath(path) # type: ignore return table @contextproperty() diff --git a/core/dbt/task/base.py b/core/dbt/task/base.py index dcf592032c4..6a7f2c493f0 100644 --- a/core/dbt/task/base.py +++ b/core/dbt/task/base.py @@ -71,6 +71,13 @@ class BaseTask(metaclass=ABCMeta): def __init__(self, args: Flags) -> None: self.args = args + def __enter__(self): + self.orig_dir = os.getcwd() + return self + + def __exit__(self, exc_type, exc_value, traceback): + os.chdir(self.orig_dir) + @abstractmethod def run(self): raise dbt_common.exceptions.base.NotImplementedError("Not Implemented") diff --git a/core/dbt/task/deps.py b/core/dbt/task/deps.py index 3e92154cf17..4c3a7134b20 100644 --- a/core/dbt/task/deps.py +++ b/core/dbt/task/deps.py @@ -96,8 +96,6 @@ def __init__(self, args: Any, project: Project) -> None: # See GH-7615 project.project_root = str(Path(project.project_root).resolve()) self.project = project - - move_to_nearest_project_dir(project.project_root) self.cli_vars = args.vars def track_package_install( @@ -202,6 +200,7 @@ def lock(self) -> None: fire_event(DepsLockUpdating(lock_filepath=lock_filepath)) def run(self) -> None: + move_to_nearest_project_dir(self.args.project_dir) if self.args.add_package: self.add() diff --git a/core/dbt/tests/util.py b/core/dbt/tests/util.py index a01ee9b67e2..3844705b974 100644 --- a/core/dbt/tests/util.py +++ b/core/dbt/tests/util.py @@ -91,6 +91,7 @@ def run_dbt( if profiles_dir and "--profiles-dir" not in args: args.extend(["--profiles-dir", profiles_dir]) dbt = dbtRunner() + res = dbt.invoke(args) # the exception is immediately raised to be caught in tests @@ -148,7 +149,7 @@ def get_manifest(project_root) -> Optional[Manifest]: if os.path.exists(path): with open(path, "rb") as fp: manifest_mp = fp.read() - manifest: Manifest = Manifest.from_msgpack(manifest_mp) + manifest: Manifest = Manifest.from_msgpack(manifest_mp) # type: ignore[attr-defined] return manifest else: return None diff --git a/tests/functional/dbt_runner/test_dbt_runner.py b/tests/functional/dbt_runner/test_dbt_runner.py index 0b1607a2eba..b2e52d1237b 100644 --- a/tests/functional/dbt_runner/test_dbt_runner.py +++ b/tests/functional/dbt_runner/test_dbt_runner.py @@ -1,3 +1,4 @@ +import os from unittest import mock import pytest @@ -103,6 +104,21 @@ def test_pass_in_args_variable(self, dbt): dbt.invoke(args) assert args == args_before + def test_directory_does_not_change(self, project, dbt: dbtRunner) -> None: + project_dir = os.getcwd() # The directory where dbt_project.yml exists. + os.chdir("../") + cmd_execution_dir = os.getcwd() # The directory where dbt command will be run + + commands = ["init", "deps", "clean"] + for command in commands: + args = [command, "--project-dir", project_dir] + if command == "init": + args.append("--skip-profile-setup") + res = dbt.invoke(args) + after_dir = os.getcwd() + assert res.success is True + assert cmd_execution_dir == after_dir + class TestDbtRunnerQueryComments: @pytest.fixture(scope="class")