Skip to content

Commit 8859071

Browse files
authored
Update supported versions matrix (#137)
* Update ci * jaffle_shop renamed to jaffle-shop-classic * Update packages * dbt >=1.7 version compatibility * Lint using Black * Fix deprecation warning * Update code compatibility: dbt>=1.7.5,<1.9.0 * Delete connections * Update pyproject * Update readme * Lint * Fix tests * Fix coverage
1 parent 8112a4c commit 8859071

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+3533
-3268
lines changed

.github/workflows/ci.yaml

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -22,32 +22,30 @@ jobs:
2222
fail-fast: false
2323
matrix:
2424
python-version:
25+
- '3.12'
2526
- '3.11'
2627
- '3.10'
2728
- '3.9'
2829
- '3.8'
2930
airflow-version:
30-
- '2.7.2'
31-
- '2.6.3'
32-
- '2.5.3'
33-
- '2.4.3'
31+
- '2.9.2'
32+
- '2.8.4'
33+
- '2.7.3'
3434
dbt-version:
35+
- 1.8
3536
- 1.7
36-
- 1.6
37-
- 1.5
38-
- 1.4
3937
exclude:
4038
# Incompatible combinations
41-
- python-version: 3.11
42-
airflow-version: '2.4.3'
39+
- python-version: 3.12
40+
airflow-version: '2.8.4'
4341

44-
- python-version: 3.11
45-
airflow-version: '2.5.3'
42+
- python-version: 3.12
43+
airflow-version: '2.7.3'
4644

4745
runs-on: ubuntu-latest
4846
steps:
4947
- name: Harden Runner
50-
uses: step-security/harden-runner@v2.6.1
48+
uses: step-security/harden-runner@v2.8.1
5149
with:
5250
egress-policy: block
5351
allowed-endpoints: >
@@ -73,34 +71,35 @@ jobs:
7371
sudo apt-get update
7472
sudo apt-get install --yes --no-install-recommends postgresql
7573
76-
- uses: actions/checkout@v4.1.1
74+
- uses: actions/checkout@v4.1.7
7775
- name: Set up Python ${{ matrix.python-version }}
78-
uses: actions/setup-python@v4.7.1
76+
uses: actions/setup-python@v5.1.0
7977
with:
8078
python-version: ${{ matrix.python-version }}
8179

8280
- name: Install Poetry
8381
uses: abatilo/actions-poetry@v2.3.0
8482
with:
85-
poetry-version: 1.7.0
83+
poetry-version: 1.8.3
8684

87-
- name: Install airflow-dbt-python with Poetry
88-
run: poetry install -E postgres --with dev
89-
90-
- name: Install Airflow with constraints
85+
- name: Install Airflow & dbt
9186
run: |
92-
wget https://raw.githubusercontent.com/apache/airflow/constraints-${{ matrix.airflow-version }}/constraints-${{ matrix.python-version }}.txt -O constraints.txt
93-
poetry run pip install apache-airflow==${{ matrix.airflow-version }} apache-airflow-providers-amazon apache-airflow-providers-ssh -c constraints.txt
94-
poetry run pip install "dbt-core~=${{ matrix.dbt-version }}.0" "dbt-postgres~=${{ matrix.dbt-version }}.0"
95-
poetry run airflow db init
87+
poetry env use ${{ matrix.python-version }}
88+
poetry add "apache-airflow==${{ matrix.airflow-version }}" \
89+
"dbt-core~=${{ matrix.dbt-version }}.0" \
90+
"dbt-postgres~=${{ matrix.dbt-version }}.0" \
91+
--python ${{ matrix.python-version }}
92+
poetry install -E postgres --with dev
93+
poetry run airflow db migrate
94+
poetry run airflow connections create-default-connections
9695
9796
- name: Linting with ruff
98-
run: poetry run ruff .
97+
run: poetry run ruff check .
9998

10099
- name: Static type checking with mypy
101100
# We only run mypy on the latest supported versions of Airflow & dbt,
102101
# so it is currently impossible to write conditions for that depend on package versions.
103-
if: matrix.airflow-version == '2.7.2' && matrix.dbt-version == '1.7'
102+
if: matrix.python-version == '3.12' && matrix.airflow-version == '2.9.2' && matrix.dbt-version == '1.8'
104103
run: poetry run mypy .
105104

106105
- name: Code formatting with black
@@ -131,7 +130,7 @@ jobs:
131130

132131
steps:
133132
- name: Harden Runner
134-
uses: step-security/harden-runner@v2.6.0
133+
uses: step-security/harden-runner@v2.8.1
135134
with:
136135
egress-policy: block
137136
allowed-endpoints: >
@@ -140,15 +139,15 @@ jobs:
140139
api.github.com:443
141140
pypi.org:443
142141
143-
- uses: actions/checkout@v4.1.1
144-
- uses: actions/setup-python@v4.7.1
142+
- uses: actions/checkout@v4.1.7
143+
- uses: actions/setup-python@v5.1.0
145144
with:
146-
python-version: '3.11'
145+
python-version: '3.12'
147146

148147
- name: Install Poetry
149148
uses: abatilo/actions-poetry@v2.3.0
150149
with:
151-
poetry-version: 1.7.0
150+
poetry-version: 1.8.3
152151

153152
- name: Install airflow-dbt-python with Poetry
154153
run: poetry install --with dev -E airflow-providers
@@ -179,7 +178,7 @@ jobs:
179178
path: htmlcov
180179

181180
- name: "Make coverage badge"
182-
uses: schneegans/dynamic-badges-action@v1.6.0
181+
uses: schneegans/dynamic-badges-action@v1.7.0
183182
if: github.event_name != 'pull_request'
184183
with:
185184
auth: ${{ secrets.GIST_TOKEN }}

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ Read the [documentation](https://airflow-dbt-python.readthedocs.io) for examples
1515
## Requirements
1616

1717
Before using *airflow-dbt-python*, ensure you meet the following requirements:
18-
* A *dbt* project using [dbt-core](https://pypi.org/project/dbt-core/) version 1.4.0 or later.
19-
* An Airflow environment using version 2.2 or later.
18+
* A *dbt* project using [dbt-core](https://pypi.org/project/dbt-core/) version 1.7.5 or later.
19+
* An Airflow environment using version 2.7 or later.
2020

21-
* If using any managed service, like AWS MWAA, ensure your environment is created with a supported version of Airflow.
21+
* If using any managed service, like AWS MWAA or GCP Cloud Composer 2/3, ensure your environment is created with a supported version of Airflow.
2222
* If self-hosting, Airflow installation instructions can be found in their [official documentation](https://airflow.apache.org/docs/apache-airflow/stable/installation/index.html).
2323

2424
* Running Python 3.8 or later in your Airflow environment.
@@ -29,7 +29,7 @@ Before using *airflow-dbt-python*, ensure you meet the following requirements:
2929
3030
> **Note**
3131
>
32-
> Older versions of Airflow and *dbt* may work with *airflow-dbt-python*, although we cannot guarantee this. Our testing pipeline runs the latest *dbt-core* with the latest Airflow release, and the latest version supported by [AWS MWAA](https://aws.amazon.com/managed-workflows-for-apache-airflow/).
32+
> Older versions of Airflow and *dbt* may work with *airflow-dbt-python*, although we cannot guarantee this. Our testing pipeline runs the latest *dbt-core* with the latest Airflow release, and the latest version supported by [AWS MWAA](https://aws.amazon.com/managed-workflows-for-apache-airflow/) and [GCP Cloud Composer 2/3](https://aws.amazon.com/managed-workflows-for-apache-airflow/).
3333
3434
## From PyPI
3535

@@ -66,6 +66,12 @@ Add *airflow-dbt-python* to your `requirements.txt` file and edit your Airflow e
6666

6767
Read the [documentation](https://airflow-dbt-python.readthedocs.io/en/latest/getting_started.html#installing-in-mwaa) for more a more detailed AWS MWAA installation breakdown.
6868

69+
## In GCP Cloud Composer
70+
71+
Add *airflow-dbt-python* to your PyPI packages list.
72+
73+
Read the [documentation](https://cloud.google.com/composer/docs/composer-2/install-python-dependencies#install-pypi) for more a more detailed GCP Cloud Composer 2 installation breakdown.
74+
6975
## In other managed services
7076

7177
*airflow-dbt-python* should be compatible with most or all Airflow managed services. Consult the documentation specific to your provider.
@@ -119,7 +125,7 @@ See an example DAG [here](examples/airflow_connection_target_dag.py).
119125

120126
Although [`dbt`](https://docs.getdbt.com/) is meant to be installed and used as a CLI, we may not have control of the environment where Airflow is running, disallowing us the option of using *dbt* as a CLI.
121127

122-
This is exactly what happens when using [Amazon's Managed Workflows for Apache Airflow](https://aws.amazon.com/managed-workflows-for-apache-airflow/) or MWAA: although a list of Python requirements can be passed, the CLI cannot be found in the worker's PATH.
128+
This is exactly what happens when using [Amazon's Managed Workflows for Apache Airflow](https://aws.amazon.com/managed-workflows-for-apache-airflow/) (aka MWAA): although a list of Python requirements can be passed, the CLI cannot be found in the worker's PATH.
123129

124130
There is a workaround which involves using Airflow's `BashOperator` and running Python from the command line:
125131

airflow_dbt_python/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
"""Provides an Airflow operator and hooks to run all or most dbtcommands."""
2+
23
from .__version__ import __author__, __copyright__, __title__, __version__

airflow_dbt_python/__version__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""The module's version information."""
2+
23
__author__ = "Tomás Farías Santana"
34
__copyright__ = "Copyright 2021 Tomás Farías Santana"
45
__title__ = "airflow-dbt-python"
5-
__version__ = "2.0.1"
6+
__version__ = "2.1.0"

airflow_dbt_python/hooks/dbt.py

Lines changed: 26 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""Provides a hook to interact with a dbt project."""
2+
23
from __future__ import annotations
34

45
import json
@@ -24,14 +25,13 @@
2425
from airflow.hooks.base import BaseHook
2526
from airflow.models.connection import Connection
2627

27-
from airflow_dbt_python.utils.version import DBT_INSTALLED_LESS_THAN_1_5
28+
from airflow_dbt_python.utils.version import DBT_INSTALLED_GTE_1_8
2829

2930
if sys.version_info >= (3, 11):
3031
from contextlib import chdir as chdir_ctx
3132
else:
3233
from contextlib_chdir import chdir as chdir_ctx
3334

34-
3535
if TYPE_CHECKING:
3636
from dbt.contracts.results import RunResult
3737
from dbt.task.base import BaseTask
@@ -227,18 +227,11 @@ def run_dbt_task(
227227
A tuple containing a boolean indicating success and optionally the results
228228
of running the dbt command.
229229
"""
230-
from dbt.adapters.factory import register_adapter
230+
from dbt.adapters.factory import adapter_management
231231
from dbt.task.base import get_nearest_project_dir
232232
from dbt.task.clean import CleanTask
233233
from dbt.task.deps import DepsTask
234-
235-
from airflow_dbt_python.utils.version import DBT_INSTALLED_LESS_THAN_1_5
236-
237-
if DBT_INSTALLED_LESS_THAN_1_5:
238-
from dbt.main import adapter_management, track_run # type: ignore
239-
else:
240-
from dbt.adapters.factory import adapter_management
241-
from dbt.tracking import track_run
234+
from dbt.tracking import track_run
242235

243236
config = self.get_dbt_task_config(command, **kwargs)
244237
extra_target = self.get_dbt_target_from_connection(config.target)
@@ -252,36 +245,24 @@ def run_dbt_task(
252245
) as dbt_dir:
253246
# When creating tasks via from_args, dbt switches to the project directory.
254247
# We have to do that here as we are not using from_args.
255-
if DBT_INSTALLED_LESS_THAN_1_5:
256-
# For compatibility with older versions of dbt, as the signature
257-
# of move_to_nearest_project_dir changed in dbt-core 1.5 to take
258-
# just the project_dir.
259-
nearest_project_dir = get_nearest_project_dir(config) # type: ignore
260-
else:
261-
nearest_project_dir = get_nearest_project_dir(config.project_dir)
248+
nearest_project_dir = get_nearest_project_dir(config.project_dir)
262249

263250
with chdir_ctx(nearest_project_dir):
264-
config.dbt_task.pre_init_hook(config)
265251
self.ensure_profiles(config)
266252

267-
task, runtime_config = config.create_dbt_task(
268-
extra_target, write_perf_info
269-
)
270-
requires_profile = isinstance(task, (CleanTask, DepsTask))
271-
272-
self.setup_dbt_logging(task, config.debug)
253+
with adapter_management():
254+
task, runtime_config = config.create_dbt_task(
255+
extra_target, write_perf_info
256+
)
257+
requires_profile = isinstance(task, (CleanTask, DepsTask))
273258

274-
if runtime_config is not None and not requires_profile:
275-
# The deps command installs the dependencies, which means they may
276-
# not exist before deps runs and the following would raise a
277-
# CompilationError.
278-
runtime_config.load_dependencies()
259+
self.setup_dbt_logging(task, config.debug)
279260

280-
results = None
281-
with adapter_management():
282-
if not requires_profile:
283-
if runtime_config is not None:
284-
register_adapter(runtime_config)
261+
if runtime_config is not None and not requires_profile:
262+
# The deps command installs the dependencies, which means they
263+
# may not exist before deps runs and the following would raise a
264+
# CompilationError.
265+
runtime_config.load_dependencies()
285266

286267
with track_run(task):
287268
results = task.run()
@@ -419,19 +400,17 @@ def setup_dbt_logging(self, task: BaseTask, debug: Optional[bool]):
419400
default_stdout. As these are initialized by the CLI app, we need to
420401
initialize them here.
421402
"""
422-
from dbt.events.functions import setup_event_logger
423-
424-
log_path = None
425-
if task.config is not None:
426-
log_path = getattr(task.config, "log_path", None)
427-
428-
if DBT_INSTALLED_LESS_THAN_1_5:
429-
setup_event_logger(log_path or "logs")
403+
if DBT_INSTALLED_GTE_1_8:
404+
from dbt.events.logging import setup_event_logger
430405
else:
431-
from dbt.flags import get_flags
406+
from dbt.events.functions import ( # type: ignore[no-redef]
407+
setup_event_logger,
408+
)
409+
410+
from dbt.flags import get_flags
432411

433-
flags = get_flags()
434-
setup_event_logger(flags)
412+
flags = get_flags()
413+
setup_event_logger(flags)
435414

436415
configured_file = logging.getLogger("configured_file")
437416
file_log = logging.getLogger("file_log")
@@ -458,7 +437,7 @@ def ensure_profiles(self, config: BaseConfig):
458437
if not profiles_path.exists():
459438
profiles_path.parent.mkdir(exist_ok=True)
460439
with profiles_path.open("w", encoding="utf-8") as f:
461-
f.write("config:\n send_anonymous_usage_stats: false\n")
440+
f.write("flags:\n send_anonymous_usage_stats: false\n")
462441

463442
def get_dbt_target_from_connection(
464443
self, target: Optional[str]

airflow_dbt_python/hooks/git.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""A concrete DbtRemoteHook for git repositories with dulwich."""
2+
23
import datetime as dt
34
from typing import Callable, Optional, Tuple, Union
45

@@ -90,7 +91,7 @@ def upload(
9091

9192
repo.stage(str(f.relative_to(source)))
9293

93-
ts = dt.datetime.utcnow()
94+
ts = dt.datetime.now(dt.timezone.utc)
9495
repo.do_commit(
9596
self.commit_msg.format(ts=ts).encode(), self.commit_author.encode()
9697
)

airflow_dbt_python/hooks/localfs.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
33
Intended to be used only when running Airflow with a LocalExceutor.
44
"""
5+
56
from __future__ import annotations
67

78
import shutil

airflow_dbt_python/hooks/remote.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
55
Currently, only AWS S3 and the local filesystem are supported as remotes.
66
"""
7+
78
from abc import ABC, abstractmethod
89
from pathlib import Path
910
from typing import Optional, Type

airflow_dbt_python/hooks/s3.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"""An implementation for an S3 remote for dbt."""
2+
23
from __future__ import annotations
34

45
from typing import Iterable, Optional

0 commit comments

Comments
 (0)