Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release v0.8.0 #90

Merged
merged 14 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# see https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners for configs

# review PRs
* @jakeberesford-palmetto @murphymoulds-palmetto @mariahjrogers @evandegler-palmetto @omoorepalmetto
* @palmetto/data-analytics @mariahjrogers
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.8.0] - 2023-11-20

### Added

- dbt version detection: palm can now detect the version of dbt used in a project
and will add the version to the plugin config. This can then be used to support
changes between dbt versions.
- support for dbt's yaml selectors via the `--selector` option on relevant commands.

### Changed

- Requires palm v2.6.0 or higher
- env vars are updated for projects running dbt v1.5.0 and later. This resolves
deprecation warnings for people using more recent versions of dbt.

## [0.7.0] - 2023-06-13

### Added
Expand Down
5 changes: 5 additions & 0 deletions palm/plugins/dbt/commands/cmd_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
@click.command("compile")
@click.option("--models", "-m", multiple=True, help="See dbt docs on models flag")
@click.option("--select", "-s", multiple=True, help="See dbt docs on select flag")
@click.option("--selector", multiple=True, help="See dbt docs on selector flag")
@click.option("--exclude", "-e", multiple=True, help="See dbt docs on exclude flag")
@click.pass_obj
def cli(
environment,
models: Optional[Tuple] = tuple(),
select: Optional[Tuple] = tuple(),
selector: Optional[Tuple] = tuple(),
exclude: Optional[Tuple] = tuple(),
):
"""Compiles the dbt repo"""
Expand All @@ -24,6 +26,9 @@ def cli(
if targets:
cmd.append("--select")
cmd.extend(targets)
if selector:
cmd.append("--selector")
cmd.extend(selector)
if exclude:
cmd.append('--exclude')
cmd.extend(exclude)
Expand Down
32 changes: 28 additions & 4 deletions palm/plugins/dbt/commands/cmd_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
@click.option("--clean", is_flag=True, help="Drop the test schema after the run")
@click.option("--models", "-m", multiple=True, help="See dbt docs on models flag")
@click.option("--select", "-s", multiple=True, help="See dbt docs on select flag")
@click.option("--selector", multiple=True, help="See dbt docs on selector flag")
@click.option("--exclude", "-e", multiple=True, help="See dbt docs on exclude flag")
@click.option("--defer", "-d", is_flag=True, help="See dbt docs on defer flag")
@click.option("--iterative", "-i", is_flag=True, help="Iterative stateful dbt run")
Expand All @@ -24,6 +25,7 @@
help="Will perform a full refresh on incremental models",
)
@click.option("--seed", is_flag=True, help="Run dbt seed before dbt run")
@click.option("--lightdash", '-ld', is_flag=True, help="Run dbt with lightdash enabled")
@click.pass_obj
def cli(
environment,
Expand All @@ -33,8 +35,10 @@ def cli(
iterative: bool,
defer: bool,
seed: bool,
lightdash: bool,
models: Optional[Tuple] = tuple(),
select: Optional[Tuple] = tuple(),
selector: Optional[Tuple] = tuple(),
exclude: Optional[Tuple] = tuple(),
vars: Optional[str] = None,
):
Expand Down Expand Up @@ -67,9 +71,11 @@ def cli(
seed=seed,
no_fail_fast=(no_fail_fast or iterative),
targets=targets,
selector=selector,
exclude=exclude,
defer=defer,
vars=vars,
lightdash=lightdash,
)

success, msg = environment.run_in_docker(run_cmd, env_vars)
Expand Down Expand Up @@ -104,9 +110,11 @@ def build_run_command(
seed: bool = True,
no_fail_fast: bool = False,
targets: Optional[list] = None,
selector: Optional[Tuple] = None,
exclude: Optional[Tuple] = None,
defer: bool = False,
vars: Optional[str] = None,
lightdash: bool = False,
) -> str:
cmd = []
full_refresh_option = " --full-refresh" if full_refresh else ""
Expand All @@ -115,10 +123,17 @@ def build_run_command(
cmd.append(f"dbt seed --full-refresh")
cmd.append("&&")

cmd.append(f"dbt run{full_refresh_option}")
if lightdash:
cmd.append(f"lightdash dbt run{full_refresh_option}")
else:
cmd.append(f"dbt run{full_refresh_option}")

if targets:
cmd.append("--select")
cmd.extend(targets)
if selector:
cmd.append("--selector")
cmd.extend(selector)
if exclude:
cmd.append("--exclude")
cmd.extend(exclude)
Expand All @@ -137,10 +152,19 @@ def build_run_command(
def set_env_vars(environment, stateful: bool, defer: bool = False) -> dict:
plugin_config = environment.plugin_config('dbt')
env_vars = dbt_env_vars(environment.palm.branch)

# These env vars are renamed in dbt v1.5.0, old env vars are deprecated
if plugin_config.is_dbt_version_greater_than("1.5.0", or_equal=True):
defer_env_var = "DBT_DEFER"
state_env_var = "DBT_STATE"
else:
defer_env_var = "DBT_DEFER_TO_STATE"
state_env_var = "DBT_ARTIFACT_STATE_PATH"

if stateful:
env_vars['DBT_DEFER_TO_STATE'] = 'true'
env_vars[defer_env_var] = 'true'
if defer:
env_vars['DBT_ARTIFACT_STATE_PATH'] = plugin_config.dbt_artifacts_prod
env_vars[state_env_var] = plugin_config.dbt_artifacts_prod
else:
env_vars['DBT_ARTIFACT_STATE_PATH'] = plugin_config.dbt_artifacts_local
env_vars[state_env_var] = plugin_config.dbt_artifacts_local
return env_vars
5 changes: 5 additions & 0 deletions palm/plugins/dbt/commands/cmd_seed.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"--clean", is_flag=True, help="drop the test schema after the run is complete"
)
@click.option("--select", '-s', multiple=True, help="see dbt docs on select flag")
@click.option("--selector", multiple=True, help="see dbt docs on selector flag")
@click.option("--exclude", '-e', multiple=True, help="see dbt docs on exclude flag")
@click.option(
"--no-full-refresh",
Expand All @@ -21,6 +22,7 @@ def cli(
clean: bool,
no_full_refresh: bool,
select: Optional[Tuple] = tuple(),
selector: Optional[Tuple] = tuple(),
exclude: Optional[Tuple] = tuple(),
):
"""Run dbt seeds"""
Expand All @@ -29,6 +31,9 @@ def cli(
if select:
cmd.append('--select')
cmd.extend(select)
if selector:
cmd.append('--selector')
cmd.extend(selector)
if exclude:
cmd.append('--exclude')
cmd.extend(exclude)
Expand Down
22 changes: 19 additions & 3 deletions palm/plugins/dbt/commands/cmd_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
)
@click.option("--models", "-m", multiple=True, help="See dbt docs on models flag")
@click.option("--select", "-s", multiple=True, help="See dbt docs on select flag")
@click.option("--selector", multiple=True, help="See dbt docs on selector flag")
@click.option("--exclude", "-e", multiple=True, help="See dbt docs on exclude flag")
@click.option("--defer", "-d", is_flag=True, help="See dbt docs on defer flag")
@click.option(
Expand All @@ -23,6 +24,7 @@ def cli(
defer: bool,
models: Optional[Tuple] = tuple(),
select: Optional[Tuple] = tuple(),
selector: Optional[Tuple] = tuple(),
exclude: Optional[Tuple] = tuple(),
):
"""Tests the dbt repo"""
Expand Down Expand Up @@ -50,6 +52,7 @@ def cli(
run_cmd = build_test_command(
no_fail_fast=no_fail_fast,
targets=targets,
selector=selector,
exclude=exclude,
defer=defer,
)
Expand All @@ -67,6 +70,7 @@ def build_test_command(
defer: bool = False,
no_fail_fast: bool = False,
targets: Optional[list] = None,
selector: Optional[Tuple] = None,
exclude: Optional[Tuple] = None,
) -> str:
cmd = []
Expand All @@ -75,6 +79,9 @@ def build_test_command(
if targets:
cmd.append('--select')
cmd.extend(targets)
if selector:
cmd.append('--selector')
cmd.extend(selector)
if exclude:
cmd.append('--exclude')
cmd.extend(exclude)
Expand All @@ -91,9 +98,18 @@ def build_test_command(
def set_env_vars(environment, defer: bool = False) -> dict:
plugin_config = environment.plugin_config('dbt')
env_vars = dbt_env_vars(environment.palm.branch)

# These env vars are renamed in dbt v1.5.0, old env vars are deprecated
if plugin_config.is_dbt_version_greater_than("1.5.0", or_equal=True):
defer_env_var = "DBT_DEFER"
state_env_var = "DBT_STATE"
else:
defer_env_var = "DBT_DEFER_TO_STATE"
state_env_var = "DBT_ARTIFACT_STATE_PATH"

if defer:
env_vars['DBT_DEFER_TO_STATE'] = 'true'
env_vars['DBT_ARTIFACT_STATE_PATH'] = plugin_config.dbt_artifacts_prod
env_vars[defer_env_var] = 'true'
env_vars[state_env_var] = plugin_config.dbt_artifacts_prod
else:
env_vars['DBT_ARTIFACT_STATE_PATH'] = plugin_config.dbt_artifacts_local
env_vars[state_env_var] = plugin_config.dbt_artifacts_local
return env_vars
69 changes: 69 additions & 0 deletions palm/plugins/dbt/dbt_version_detection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import click
from pathlib import Path
import yaml

from palm.utils import run_in_docker


def get_dbt_version() -> str:
"""Get the dbt version from the dbt-core package in the docker image.

Returns:
str: The dbt version
"""
image_name = _get_image_name()
cmd = "pip list | grep dbt-core"
success, msg = run_in_docker(cmd, image_name, capture_output=True, silent=True)
if not success:
raise ValueError(f"Error getting dbt version: {msg}")
return msg.strip().split(" ")[-1]


def dbt_version_factory() -> str:
"""Get the dbt version from the dbt-core package in the docker image.
Then update the dbt version in the .palm/config.yaml file.

Returns:
str: The dbt version
"""
click.secho("Detecting dbt version...", fg="yellow")
version = get_dbt_version()
set_dbt_version_in_config(version)
click.secho(
f"Detected dbt version: {version}. .palm/config.yml updated", fg="green"
)
return version


def set_dbt_version_in_config(version: str) -> None:
"""Update the dbt version in the .palm/config.yaml file."""
palm_config = _get_palm_config()
palm_config['plugin_config']['dbt']['dbt_version'] = version
_update_palm_config(palm_config)


def _get_image_name():
"""Get the image name from the .palm/config.yaml file."""
palm_config = _get_palm_config()
return palm_config["image_name"]


# Ideally, the 2 functions below would come from palm_config.py in palm-cli
# However, that class is more complicated than it should be and needs a refactor.
# So, for now, we have to do this here.


def _get_palm_config() -> dict:
"""Get the palm config from the .palm/config.yaml file."""
palm_config_path = Path(".palm/config.yaml")
if not palm_config_path.exists():
raise ValueError("No .palm/config.yaml file found")
return yaml.safe_load(palm_config_path.read_text())


def _update_palm_config(config: dict) -> None:
"""Update the palm config in the .palm/config.yaml file."""
palm_config_path = Path(".palm/config.yaml")
if not palm_config_path.exists():
raise ValueError("No .palm/config.yaml file found")
palm_config_path.write_text(yaml.dump(config))
27 changes: 25 additions & 2 deletions palm/plugins/dbt/plugin_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,32 @@

from typing import Optional
from pathlib import Path
from pydantic import BaseModel
import yaml
import semver
from pydantic import BaseModel, Field
from palm.plugins.base_plugin_config import BasePluginConfig
from palm.plugins.dbt.dbt_version_detection import dbt_version_factory, get_dbt_version


class dbtPluginConfigModel(BaseModel):
dbt_artifacts_prod: Optional[str]
dbt_artifacts_local: str
dbt_version: str = Field(default_factory=dbt_version_factory)

def dbt_version_semver(self) -> str:
return semver.Version.parse(self.dbt_version)

def is_dbt_version_greater_than(self, version: str, or_equal: bool = True) -> bool:
target_version = semver.Version.parse(version)
if or_equal:
return self.dbt_version_semver() >= target_version
return self.dbt_version_semver() > target_version

def is_dbt_version_less_than(self, version: str, or_equal: bool = True) -> bool:
target_version = semver.Version.parse(version)
if or_equal:
return self.dbt_version_semver() <= target_version
return self.dbt_version_semver() < target_version


class DbtPluginConfig(BasePluginConfig):
Expand All @@ -23,7 +42,9 @@ def set(self) -> dict:
)
if has_prod_artifacts:
prod = click.prompt(
"Prod artifacts location:", type=click.Path(exists=True)
"Prod artifacts location:",
type=click.Path(exists=True),
default=Path("target/prod_artifacts"),
)
config["dbt_artifacts_prod"] = str(prod)
click.secho(f"Saved prod artifacts location: {prod}", fg="green")
Expand All @@ -35,4 +56,6 @@ def set(self) -> dict:
)
config['dbt_artifacts_local'] = str(local)

config['dbt_version'] = get_dbt_version()

return config
3 changes: 2 additions & 1 deletion palm/plugins/dbt/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
palm >=2.5.1, <3.0
palm >=2.6.0, <3.0
pyyaml >= 5.0
sqlparse >= 0.3.1
semver >= 3.0.1
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

setup(
name='palm-dbt',
version='0.7.0',
version='0.8.0',
description='dbt extension for Palm CLI',
long_description=long_description,
long_description_content_type='text/markdown',
Expand Down
Loading