dbt-bouncer 0.14.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dbt_bouncer-0.14.0/LICENSE +21 -0
- dbt_bouncer-0.14.0/PKG-INFO +196 -0
- dbt_bouncer-0.14.0/README.md +164 -0
- dbt_bouncer-0.14.0/dbt_bouncer/checks/checks.md +168 -0
- dbt_bouncer-0.14.0/dbt_bouncer/checks/conftest.py +20 -0
- dbt_bouncer-0.14.0/dbt_bouncer/checks/manifest/check_macros.py +47 -0
- dbt_bouncer-0.14.0/dbt_bouncer/checks/manifest/check_metadata.py +18 -0
- dbt_bouncer-0.14.0/dbt_bouncer/checks/manifest/check_models.py +33 -0
- dbt_bouncer-0.14.0/dbt_bouncer/checks/manifest/check_project_directories.py +18 -0
- dbt_bouncer-0.14.0/dbt_bouncer/checks/manifest/check_sources.py +33 -0
- dbt_bouncer-0.14.0/dbt_bouncer/config_validator.py +93 -0
- dbt_bouncer-0.14.0/dbt_bouncer/github.py +101 -0
- dbt_bouncer-0.14.0/dbt_bouncer/logger.py +34 -0
- dbt_bouncer-0.14.0/dbt_bouncer/main.py +114 -0
- dbt_bouncer-0.14.0/dbt_bouncer/runner.py +71 -0
- dbt_bouncer-0.14.0/dbt_bouncer/runner_plugins.py +139 -0
- dbt_bouncer-0.14.0/dbt_bouncer/utils.py +71 -0
- dbt_bouncer-0.14.0/dbt_bouncer/version.py +2 -0
- dbt_bouncer-0.14.0/pyproject.toml +99 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Pádraic Slattery
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: dbt-bouncer
|
|
3
|
+
Version: 0.14.0
|
|
4
|
+
Summary: Configure and enforce conventions for your dbt project.
|
|
5
|
+
Home-page: https://github.com/godatadriven/dbt-bouncer
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: python,cli,dbt,CI/CD
|
|
8
|
+
Author: Padraic Slattery
|
|
9
|
+
Author-email: pgoslatara@gmail.com
|
|
10
|
+
Maintainer: Padraic Slattery
|
|
11
|
+
Maintainer-email: pgoslatara@gmail.com
|
|
12
|
+
Requires-Python: >=3.8.1,<3.13
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
22
|
+
Requires-Dist: click (<9)
|
|
23
|
+
Requires-Dist: dbt-artifacts-parser (>=0,<1)
|
|
24
|
+
Requires-Dist: pydantic (>=2,<3)
|
|
25
|
+
Requires-Dist: pytest (<9)
|
|
26
|
+
Requires-Dist: pyyaml (<7)
|
|
27
|
+
Requires-Dist: requests (>=2,<3)
|
|
28
|
+
Requires-Dist: tabulate (>=0,<1)
|
|
29
|
+
Project-URL: Repository, https://github.com/godatadriven/dbt-bouncer
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
<p align="center">
|
|
33
|
+
<img src="./images/logo.webp" alt="dbt-bouncer logo" width="500"/>
|
|
34
|
+
</p>
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
<h1 align="center">
|
|
38
|
+
dbt-bouncer
|
|
39
|
+
</h1>
|
|
40
|
+
<h2 align="center">
|
|
41
|
+
Configure and enforce conventions for your dbt project.
|
|
42
|
+
</h2>
|
|
43
|
+
|
|
44
|
+
<div align="center">
|
|
45
|
+
<a>
|
|
46
|
+
<img src="https://img.shields.io/github/release/godatadriven/dbt-bouncer.svg?logo=github">
|
|
47
|
+
</a>
|
|
48
|
+
<a>
|
|
49
|
+
<img src="https://github.com/godatadriven/dbt-bouncer/actions/workflows/ci_pipeline.yml/badge.svg">
|
|
50
|
+
</a>
|
|
51
|
+
<a>
|
|
52
|
+
<img src="https://img.shields.io/badge/License-MIT-yellow.svg">
|
|
53
|
+
</a>
|
|
54
|
+
<a>
|
|
55
|
+
<img src="https://img.shields.io/github/last-commit/godatadriven/dbt-bouncer/main">
|
|
56
|
+
</a>
|
|
57
|
+
<a>
|
|
58
|
+
<img src="https://img.shields.io/github/commits-since/godatadriven/dbt-bouncer/latest">
|
|
59
|
+
</a>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div align="center">
|
|
63
|
+
<a>
|
|
64
|
+
<img alt="dbt" src="https://img.shields.io/badge/dbt%20-%3E%3D1.6-333?logo=dbt">
|
|
65
|
+
</a>
|
|
66
|
+
<a>
|
|
67
|
+
<img alt="Docker Supported" src="https://img.shields.io/badge/Docker%20-Supported-0db7ed?logo=docker">
|
|
68
|
+
</a>
|
|
69
|
+
<a>
|
|
70
|
+
<img alt="GitHub Supported" src="https://img.shields.io/badge/GitHub%20-Supported-333?logo=github">
|
|
71
|
+
</a>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<div align="center">
|
|
75
|
+
<a>
|
|
76
|
+
<img src="https://img.shields.io/badge/code%20style-black-000000.svg">
|
|
77
|
+
</a>
|
|
78
|
+
<a>
|
|
79
|
+
<img src="https://www.aschey.tech/tokei/github/godatadriven/dbt-bouncer?category=code">
|
|
80
|
+
</a>
|
|
81
|
+
</div>
|
|
82
|
+
<br/>
|
|
83
|
+
|
|
84
|
+
# How to use
|
|
85
|
+
|
|
86
|
+
1. Generate a `manifest.json` by running `dbt parse`.
|
|
87
|
+
1. Create a `dbt-bouncer.yml` config file, details [here](#config-file).
|
|
88
|
+
1. Run `dbt-bouncer` to validate that your conventions are being maintained. You can use GitHub Actions, Docker, a `.pex` file or python to run `dbt-bouncer`.
|
|
89
|
+
|
|
90
|
+
## GitHub Actions
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
steps:
|
|
94
|
+
...
|
|
95
|
+
|
|
96
|
+
- uses: godatadriven/dbt-bouncer@v0
|
|
97
|
+
with:
|
|
98
|
+
config-file: ./<PATH_TO_CONFIG_FILE>
|
|
99
|
+
send-pr-comment: true # optional, defaults to true
|
|
100
|
+
|
|
101
|
+
...
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Docker
|
|
105
|
+
|
|
106
|
+
Don't use GitHub Actions? You can still use dbt-bouncer via Docker:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
docker run --rm \
|
|
110
|
+
--volume "$PWD":/app \
|
|
111
|
+
ghcr.io/godatadriven/dbt-bouncer:vX.X.X \
|
|
112
|
+
--config-file /app/<PATH_TO_CONFIG_FILE>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Pex
|
|
116
|
+
|
|
117
|
+
You can also run the `.pex` ([Python EXecutable](https://docs.pex-tool.org/whatispex.html#whatispex)) artifact directly once you have a python executable installed:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
wget https://github.com/godatadriven/dbt-bouncer/releases/download/vX.X.X/dbt-bouncer.pex -O dbt-bouncer.pex
|
|
121
|
+
|
|
122
|
+
dbt-bouncer.pex --config-file $PWD/<PATH_TO_CONFIG_FILE>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Python
|
|
126
|
+
|
|
127
|
+
Install from [pypi.org](https://pypi.org/dbt-bouncer):
|
|
128
|
+
|
|
129
|
+
```shell
|
|
130
|
+
pip install dbt-bouncer
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Run:
|
|
134
|
+
```shell
|
|
135
|
+
dbt-bouncer.pex --config-file $PWD/<PATH_TO_CONFIG_FILE>
|
|
136
|
+
``
|
|
137
|
+
|
|
138
|
+
# Config file
|
|
139
|
+
|
|
140
|
+
`dbt-bouncer` requires a config file to be provided. This file configures what checks are run. Here is an example config file:
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
dbt-artifacts-dir: target # [Optional] Directory where the dbt artifacts exists, generally the `target` directory inside a dbt project. Defaults to `./target`.
|
|
144
|
+
|
|
145
|
+
manifest_checks:
|
|
146
|
+
- name: check_macro_name_matches_file_name
|
|
147
|
+
- name: check_model_names
|
|
148
|
+
include: ^staging
|
|
149
|
+
model_name_pattern: ^stg_
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
# Checks
|
|
153
|
+
|
|
154
|
+
:bulb: Click on a check name to see more details.
|
|
155
|
+
|
|
156
|
+
**Macros**
|
|
157
|
+
|
|
158
|
+
* [`check_macro_arguments_description_populated`](./dbt_bouncer/checks/checks.md#check_macro_arguments_description_populated): Macro arguments must have a populated description.
|
|
159
|
+
* [`check_macro_description_populated`](./dbt_bouncer/checks/checks.md#check_macro_description_populated): Macros must have a populated description.
|
|
160
|
+
* [`check_macro_name_matches_file_name`](./dbt_bouncer/checks/checks.md#check_macro_name_matches_file_name): Macros names must be the same as the file they are contained in.
|
|
161
|
+
|
|
162
|
+
**Metadata**
|
|
163
|
+
|
|
164
|
+
* [`check_project_name`](./dbt_bouncer/checks/checks.md#check_project_name): Enforce that the name of the dbt project matches a supplied regex.
|
|
165
|
+
|
|
166
|
+
**Miscellaneous**
|
|
167
|
+
|
|
168
|
+
* [`check_top_level_directories`](./dbt_bouncer/checks/checks.md#check_top_level_directories): Only specified top-level directories are allowed to contain models.
|
|
169
|
+
|
|
170
|
+
**Models**
|
|
171
|
+
|
|
172
|
+
* [`check_model_description_populated`](./dbt_bouncer/checks/checks.md#check_model_description_populated): Models must have a populated description.
|
|
173
|
+
* [`check_model_names`](./dbt_bouncer/checks/checks.md#check_model_names): Models must have a name that matches the supplied regex.
|
|
174
|
+
|
|
175
|
+
**Sources**
|
|
176
|
+
|
|
177
|
+
* [`check_source_has_meta_keys`](./dbt_bouncer/checks/checks.md#check_source_has_meta_keys): The `meta` config for sources must have the specified keys.
|
|
178
|
+
|
|
179
|
+
# Development
|
|
180
|
+
|
|
181
|
+
To setup your development environment, fork this repository and run:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
poetry shell
|
|
185
|
+
poetry install
|
|
186
|
+
pre-commit install
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Set required environment variables by copying `.env.example` to `.env` and updating the values.
|
|
190
|
+
|
|
191
|
+
All tests can be run via:
|
|
192
|
+
```bash
|
|
193
|
+
make build-artifacts # Rebuilds dbt artifacts used by pytest
|
|
194
|
+
make test
|
|
195
|
+
```
|
|
196
|
+
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="./images/logo.webp" alt="dbt-bouncer logo" width="500"/>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
<h1 align="center">
|
|
7
|
+
dbt-bouncer
|
|
8
|
+
</h1>
|
|
9
|
+
<h2 align="center">
|
|
10
|
+
Configure and enforce conventions for your dbt project.
|
|
11
|
+
</h2>
|
|
12
|
+
|
|
13
|
+
<div align="center">
|
|
14
|
+
<a>
|
|
15
|
+
<img src="https://img.shields.io/github/release/godatadriven/dbt-bouncer.svg?logo=github">
|
|
16
|
+
</a>
|
|
17
|
+
<a>
|
|
18
|
+
<img src="https://github.com/godatadriven/dbt-bouncer/actions/workflows/ci_pipeline.yml/badge.svg">
|
|
19
|
+
</a>
|
|
20
|
+
<a>
|
|
21
|
+
<img src="https://img.shields.io/badge/License-MIT-yellow.svg">
|
|
22
|
+
</a>
|
|
23
|
+
<a>
|
|
24
|
+
<img src="https://img.shields.io/github/last-commit/godatadriven/dbt-bouncer/main">
|
|
25
|
+
</a>
|
|
26
|
+
<a>
|
|
27
|
+
<img src="https://img.shields.io/github/commits-since/godatadriven/dbt-bouncer/latest">
|
|
28
|
+
</a>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div align="center">
|
|
32
|
+
<a>
|
|
33
|
+
<img alt="dbt" src="https://img.shields.io/badge/dbt%20-%3E%3D1.6-333?logo=dbt">
|
|
34
|
+
</a>
|
|
35
|
+
<a>
|
|
36
|
+
<img alt="Docker Supported" src="https://img.shields.io/badge/Docker%20-Supported-0db7ed?logo=docker">
|
|
37
|
+
</a>
|
|
38
|
+
<a>
|
|
39
|
+
<img alt="GitHub Supported" src="https://img.shields.io/badge/GitHub%20-Supported-333?logo=github">
|
|
40
|
+
</a>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<div align="center">
|
|
44
|
+
<a>
|
|
45
|
+
<img src="https://img.shields.io/badge/code%20style-black-000000.svg">
|
|
46
|
+
</a>
|
|
47
|
+
<a>
|
|
48
|
+
<img src="https://www.aschey.tech/tokei/github/godatadriven/dbt-bouncer?category=code">
|
|
49
|
+
</a>
|
|
50
|
+
</div>
|
|
51
|
+
<br/>
|
|
52
|
+
|
|
53
|
+
# How to use
|
|
54
|
+
|
|
55
|
+
1. Generate a `manifest.json` by running `dbt parse`.
|
|
56
|
+
1. Create a `dbt-bouncer.yml` config file, details [here](#config-file).
|
|
57
|
+
1. Run `dbt-bouncer` to validate that your conventions are being maintained. You can use GitHub Actions, Docker, a `.pex` file or python to run `dbt-bouncer`.
|
|
58
|
+
|
|
59
|
+
## GitHub Actions
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
steps:
|
|
63
|
+
...
|
|
64
|
+
|
|
65
|
+
- uses: godatadriven/dbt-bouncer@v0
|
|
66
|
+
with:
|
|
67
|
+
config-file: ./<PATH_TO_CONFIG_FILE>
|
|
68
|
+
send-pr-comment: true # optional, defaults to true
|
|
69
|
+
|
|
70
|
+
...
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Docker
|
|
74
|
+
|
|
75
|
+
Don't use GitHub Actions? You can still use dbt-bouncer via Docker:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
docker run --rm \
|
|
79
|
+
--volume "$PWD":/app \
|
|
80
|
+
ghcr.io/godatadriven/dbt-bouncer:vX.X.X \
|
|
81
|
+
--config-file /app/<PATH_TO_CONFIG_FILE>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Pex
|
|
85
|
+
|
|
86
|
+
You can also run the `.pex` ([Python EXecutable](https://docs.pex-tool.org/whatispex.html#whatispex)) artifact directly once you have a python executable installed:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
wget https://github.com/godatadriven/dbt-bouncer/releases/download/vX.X.X/dbt-bouncer.pex -O dbt-bouncer.pex
|
|
90
|
+
|
|
91
|
+
dbt-bouncer.pex --config-file $PWD/<PATH_TO_CONFIG_FILE>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Python
|
|
95
|
+
|
|
96
|
+
Install from [pypi.org](https://pypi.org/dbt-bouncer):
|
|
97
|
+
|
|
98
|
+
```shell
|
|
99
|
+
pip install dbt-bouncer
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Run:
|
|
103
|
+
```shell
|
|
104
|
+
dbt-bouncer.pex --config-file $PWD/<PATH_TO_CONFIG_FILE>
|
|
105
|
+
``
|
|
106
|
+
|
|
107
|
+
# Config file
|
|
108
|
+
|
|
109
|
+
`dbt-bouncer` requires a config file to be provided. This file configures what checks are run. Here is an example config file:
|
|
110
|
+
|
|
111
|
+
```yaml
|
|
112
|
+
dbt-artifacts-dir: target # [Optional] Directory where the dbt artifacts exists, generally the `target` directory inside a dbt project. Defaults to `./target`.
|
|
113
|
+
|
|
114
|
+
manifest_checks:
|
|
115
|
+
- name: check_macro_name_matches_file_name
|
|
116
|
+
- name: check_model_names
|
|
117
|
+
include: ^staging
|
|
118
|
+
model_name_pattern: ^stg_
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
# Checks
|
|
122
|
+
|
|
123
|
+
:bulb: Click on a check name to see more details.
|
|
124
|
+
|
|
125
|
+
**Macros**
|
|
126
|
+
|
|
127
|
+
* [`check_macro_arguments_description_populated`](./dbt_bouncer/checks/checks.md#check_macro_arguments_description_populated): Macro arguments must have a populated description.
|
|
128
|
+
* [`check_macro_description_populated`](./dbt_bouncer/checks/checks.md#check_macro_description_populated): Macros must have a populated description.
|
|
129
|
+
* [`check_macro_name_matches_file_name`](./dbt_bouncer/checks/checks.md#check_macro_name_matches_file_name): Macros names must be the same as the file they are contained in.
|
|
130
|
+
|
|
131
|
+
**Metadata**
|
|
132
|
+
|
|
133
|
+
* [`check_project_name`](./dbt_bouncer/checks/checks.md#check_project_name): Enforce that the name of the dbt project matches a supplied regex.
|
|
134
|
+
|
|
135
|
+
**Miscellaneous**
|
|
136
|
+
|
|
137
|
+
* [`check_top_level_directories`](./dbt_bouncer/checks/checks.md#check_top_level_directories): Only specified top-level directories are allowed to contain models.
|
|
138
|
+
|
|
139
|
+
**Models**
|
|
140
|
+
|
|
141
|
+
* [`check_model_description_populated`](./dbt_bouncer/checks/checks.md#check_model_description_populated): Models must have a populated description.
|
|
142
|
+
* [`check_model_names`](./dbt_bouncer/checks/checks.md#check_model_names): Models must have a name that matches the supplied regex.
|
|
143
|
+
|
|
144
|
+
**Sources**
|
|
145
|
+
|
|
146
|
+
* [`check_source_has_meta_keys`](./dbt_bouncer/checks/checks.md#check_source_has_meta_keys): The `meta` config for sources must have the specified keys.
|
|
147
|
+
|
|
148
|
+
# Development
|
|
149
|
+
|
|
150
|
+
To setup your development environment, fork this repository and run:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
poetry shell
|
|
154
|
+
poetry install
|
|
155
|
+
pre-commit install
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Set required environment variables by copying `.env.example` to `.env` and updating the values.
|
|
159
|
+
|
|
160
|
+
All tests can be run via:
|
|
161
|
+
```bash
|
|
162
|
+
make build-artifacts # Rebuilds dbt artifacts used by pytest
|
|
163
|
+
make test
|
|
164
|
+
```
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# `check_macro_arguments_description_populated`
|
|
2
|
+
|
|
3
|
+
Macro arguments must have a populated description.
|
|
4
|
+
|
|
5
|
+
**Argument(s)**:
|
|
6
|
+
|
|
7
|
+
* `include`: (Optional) Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
|
|
8
|
+
|
|
9
|
+
**Example**:
|
|
10
|
+
```yaml
|
|
11
|
+
checks:
|
|
12
|
+
- name: check_macro_arguments_description_populated
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**Required artifact(s)**:
|
|
16
|
+
|
|
17
|
+
* manifest.json
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# `check_macro_description_populated`
|
|
22
|
+
|
|
23
|
+
Macros must have a populated description.
|
|
24
|
+
|
|
25
|
+
**Argument(s)**:
|
|
26
|
+
|
|
27
|
+
* `include`: (Optional) Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
|
|
28
|
+
|
|
29
|
+
**Example**:
|
|
30
|
+
```yaml
|
|
31
|
+
checks:
|
|
32
|
+
- name: check_macro_description_populated
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**Required artifacts(s)**:
|
|
36
|
+
|
|
37
|
+
* manifest.json
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
# `check_macro_name_matches_file_name`
|
|
42
|
+
|
|
43
|
+
Macros names must be the same as the file they are contained in.
|
|
44
|
+
|
|
45
|
+
Generic tests are also macros, however to document these tests the "name" value must be precededed with "test_".
|
|
46
|
+
|
|
47
|
+
**Argument(s)**:
|
|
48
|
+
|
|
49
|
+
* `include`: (Optional) Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
|
|
50
|
+
|
|
51
|
+
**Example**:
|
|
52
|
+
```yaml
|
|
53
|
+
checks:
|
|
54
|
+
- name: check_macro_name_matches_file_name
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Required artifacts(s)**:
|
|
58
|
+
|
|
59
|
+
* manifest.json
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
# `check_model_description_populated`
|
|
64
|
+
|
|
65
|
+
Models must have a populated description.
|
|
66
|
+
|
|
67
|
+
**Argument(s)**:
|
|
68
|
+
|
|
69
|
+
* `include`: (Optional) Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
|
|
70
|
+
|
|
71
|
+
**Example**:
|
|
72
|
+
```yaml
|
|
73
|
+
checks:
|
|
74
|
+
- name: check_model_description_populated
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Required artifacts(s)**:
|
|
78
|
+
|
|
79
|
+
* manifest.json
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
# `check_model_names`
|
|
84
|
+
|
|
85
|
+
Models must have a name that matches the supplied regex.
|
|
86
|
+
|
|
87
|
+
**Argument(s)**:
|
|
88
|
+
|
|
89
|
+
* `include`: (Optional) Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
|
|
90
|
+
* `model_name_pattern`: Regex pattern to match the model name.
|
|
91
|
+
|
|
92
|
+
**Example**:
|
|
93
|
+
```yaml
|
|
94
|
+
checks:
|
|
95
|
+
- name: check_model_names
|
|
96
|
+
include: ^intermediate
|
|
97
|
+
model_name_pattern: ^int_
|
|
98
|
+
- name: check_model_names
|
|
99
|
+
include: ^staging
|
|
100
|
+
model_name_pattern: ^stg_
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**Required artifacts(s)**:
|
|
104
|
+
|
|
105
|
+
* manifest.json
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
# `check_project_name`
|
|
110
|
+
|
|
111
|
+
Enforce that the name of the dbt project matches a supplied regex. Generally used to enforce that project names conform to something like `company_<DOMAIN>`.
|
|
112
|
+
|
|
113
|
+
**Argument(s)**:
|
|
114
|
+
|
|
115
|
+
* `include`: (Optional) Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
|
|
116
|
+
* `project_name_pattern`: Regex pattern to match the project name.
|
|
117
|
+
|
|
118
|
+
**Example**:
|
|
119
|
+
```yaml
|
|
120
|
+
checks:
|
|
121
|
+
- name: check_project_name
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Required artifacts(s)**:
|
|
125
|
+
|
|
126
|
+
* manifest.json
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
# `check_source_has_meta_keys`
|
|
131
|
+
|
|
132
|
+
The `meta` config for sources must have the specified keys.
|
|
133
|
+
|
|
134
|
+
**Argument(s)**:
|
|
135
|
+
|
|
136
|
+
* `include`: (Optional) Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
|
|
137
|
+
* `key`:
|
|
138
|
+
|
|
139
|
+
**Example**:
|
|
140
|
+
```yaml
|
|
141
|
+
checks:
|
|
142
|
+
- name: check_source_has_meta_keys
|
|
143
|
+
keys:
|
|
144
|
+
- contact:
|
|
145
|
+
- email
|
|
146
|
+
- slack
|
|
147
|
+
- owner
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Required artifacts(s)**:
|
|
151
|
+
|
|
152
|
+
* manifest.json
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
# `check_top_level_directories`
|
|
157
|
+
|
|
158
|
+
Only specified top-level directories are allowed to contain models.
|
|
159
|
+
|
|
160
|
+
**Example**:
|
|
161
|
+
```yaml
|
|
162
|
+
checks:
|
|
163
|
+
- name: check_top_level_directories
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Required artifacts(s)**:
|
|
167
|
+
|
|
168
|
+
* manifest.json
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
def pytest_configure(config):
|
|
2
|
+
config.addinivalue_line("markers", "iterate_over_macros: Tests that should run once per macro")
|
|
3
|
+
config.addinivalue_line(
|
|
4
|
+
"markers",
|
|
5
|
+
"iterate_over_models: Tests that should run once per model",
|
|
6
|
+
)
|
|
7
|
+
config.addinivalue_line(
|
|
8
|
+
"markers",
|
|
9
|
+
"iterate_over_sources: Tests that should run once per source",
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
ini_values = {
|
|
13
|
+
"python_classes": ["check_", "Check"],
|
|
14
|
+
"python_files": ["check_*.py"],
|
|
15
|
+
"python_functions": ["check_*"],
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
for name, values in ini_values.items():
|
|
19
|
+
current = config.getini(name)
|
|
20
|
+
current[:] = values
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from dbt_bouncer.utils import get_check_inputs
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.mark.iterate_over_macros
|
|
7
|
+
def check_macro_arguments_description_populated(request, check_config=None, macro=None) -> None:
|
|
8
|
+
"""
|
|
9
|
+
Macro arguments must have a populated description.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
macro = get_check_inputs(check_config=check_config, macro=macro, request=request)["macro"]
|
|
13
|
+
for arg in macro["arguments"]:
|
|
14
|
+
assert (
|
|
15
|
+
len(arg["description"].strip()) > 4
|
|
16
|
+
), f"Argument {arg['name']} in {macro['unique_id']} does not have a populated description."
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.mark.iterate_over_macros
|
|
20
|
+
def check_macro_description_populated(request, check_config=None, macro=None) -> None:
|
|
21
|
+
"""
|
|
22
|
+
Macros must have a populated description.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
macro = get_check_inputs(check_config=check_config, macro=macro, request=request)["macro"]
|
|
26
|
+
assert (
|
|
27
|
+
len(macro["description"].strip()) > 4
|
|
28
|
+
), f"{macro['unique_id']} does not have a populated description."
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.mark.iterate_over_macros
|
|
32
|
+
def check_macro_name_matches_file_name(request, check_config=None, macro=None) -> None:
|
|
33
|
+
"""
|
|
34
|
+
Macros names must be the same as the file they are contained in.
|
|
35
|
+
|
|
36
|
+
Generic tests are also macros, however to document these tests the "name" value must be precededed with "test_".
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
macro = get_check_inputs(macro=macro, request=request)["macro"]
|
|
40
|
+
if macro["name"].startswith("test_"):
|
|
41
|
+
assert (
|
|
42
|
+
macro["name"][5:] == macro["path"].split("/")[-1].split(".")[0]
|
|
43
|
+
), f"{macro['unique_id']} is not in a file named `{macro['name'][5:]}.sql`."
|
|
44
|
+
else:
|
|
45
|
+
assert (
|
|
46
|
+
macro["name"] == macro["path"].split("/")[-1].split(".")[0]
|
|
47
|
+
), f"{macro['unique_id']} is not in a file of the same name."
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from dbt_bouncer.utils import get_check_inputs
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def check_project_name(manifest_obj, request, check_config=None):
|
|
7
|
+
"""
|
|
8
|
+
Enforce that the name of the dbt project matches a supplied regex. Generally used to enforce that project names conform to something like `company_<DOMAIN>`.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
check_config = get_check_inputs(check_config=check_config, request=request)["check_config"]
|
|
12
|
+
|
|
13
|
+
assert (
|
|
14
|
+
re.compile(check_config["project_name_pattern"].strip()).match(
|
|
15
|
+
manifest_obj.metadata.project_name
|
|
16
|
+
)
|
|
17
|
+
is not None
|
|
18
|
+
), f"Project name (`{manifest_obj.metadata.project_name}`) does not conform to the supplied regex `({check_config['project_name_pattern'].strip()})`."
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from dbt_bouncer.utils import get_check_inputs
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.iterate_over_models
|
|
9
|
+
def check_model_description_populated(request, check_config=None, model=None):
|
|
10
|
+
"""
|
|
11
|
+
Models must have a populated description.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
model = get_check_inputs(model=model, request=request)["model"]
|
|
15
|
+
|
|
16
|
+
assert (
|
|
17
|
+
len(model["description"].strip()) > 4
|
|
18
|
+
), f"{model['unique_id']} does not have a populated description."
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.mark.iterate_over_models
|
|
22
|
+
def check_model_names(request, check_config=None, model=None):
|
|
23
|
+
"""
|
|
24
|
+
Models must have a name that matches the supplied regex.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
input_vars = get_check_inputs(check_config=check_config, model=model, request=request)
|
|
28
|
+
check_config = input_vars["check_config"]
|
|
29
|
+
model = input_vars["model"]
|
|
30
|
+
|
|
31
|
+
assert (
|
|
32
|
+
re.compile(check_config["model_name_pattern"].strip()).match(model["name"]) is not None
|
|
33
|
+
), f"{model['unique_id']} does not match the supplied regex `({check_config['model_name_pattern'].strip()})`."
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from dbt_bouncer.utils import get_check_inputs
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.mark.iterate_over_models
|
|
7
|
+
def check_top_level_directories(request, model=None):
|
|
8
|
+
"""
|
|
9
|
+
Only specified top-level directories are allowed to contain models.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
model = get_check_inputs(model=model, request=request)["model"]
|
|
13
|
+
top_level_dir = model["path"].split("/")[0]
|
|
14
|
+
assert top_level_dir in [
|
|
15
|
+
"staging",
|
|
16
|
+
"intermediate",
|
|
17
|
+
"marts",
|
|
18
|
+
], f"{model['unique_id']} is located in `{top_level_dir}`, this is not a valid top-level directory."
|