pipelineprobe 0.3.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.
- pipelineprobe-0.3.0/.dockerignore +31 -0
- pipelineprobe-0.3.0/.github/workflows/ci.yml +63 -0
- pipelineprobe-0.3.0/.github/workflows/pages.yml +50 -0
- pipelineprobe-0.3.0/.github/workflows/release.yml +59 -0
- pipelineprobe-0.3.0/.gitignore +171 -0
- pipelineprobe-0.3.0/.vscode/settings.json +1 -0
- pipelineprobe-0.3.0/CHANGELOG.md +130 -0
- pipelineprobe-0.3.0/CONTRIBUTING.md +189 -0
- pipelineprobe-0.3.0/Dockerfile +18 -0
- pipelineprobe-0.3.0/LICENSE +21 -0
- pipelineprobe-0.3.0/PKG-INFO +148 -0
- pipelineprobe-0.3.0/README.md +122 -0
- pipelineprobe-0.3.0/docs/architecture.md +194 -0
- pipelineprobe-0.3.0/docs/assets/pipelineprobe-demo.gif +0 -0
- pipelineprobe-0.3.0/docs/bugs_improvements.md +65 -0
- pipelineprobe-0.3.0/docs/ci-integration.md +155 -0
- pipelineprobe-0.3.0/docs/configuration.md +217 -0
- pipelineprobe-0.3.0/docs/report-screenshot.png +0 -0
- pipelineprobe-0.3.0/docs/requirement.md +403 -0
- pipelineprobe-0.3.0/docs/superpowers/plans/2026-11-15-pipelineprobe-v0.3.0.md +118 -0
- pipelineprobe-0.3.0/examples/github-actions/pipelineprobe.yml +40 -0
- pipelineprobe-0.3.0/examples/quickstart/README.md +128 -0
- pipelineprobe-0.3.0/examples/quickstart/dags/demo_pipelines.py +60 -0
- pipelineprobe-0.3.0/examples/quickstart/dbt_project/dbt_project.yml +28 -0
- pipelineprobe-0.3.0/examples/quickstart/dbt_project/target/manifest.json +313 -0
- pipelineprobe-0.3.0/examples/quickstart/dbt_project/target/run_results.json +124 -0
- pipelineprobe-0.3.0/examples/quickstart/docker-compose.yml +114 -0
- pipelineprobe-0.3.0/examples/quickstart/init-warehouse.sql +96 -0
- pipelineprobe-0.3.0/examples/quickstart/pipelineprobe.yml +27 -0
- pipelineprobe-0.3.0/examples/quickstart/reports/.gitkeep +0 -0
- pipelineprobe-0.3.0/examples/quickstart/run-audit.sh +304 -0
- pipelineprobe-0.3.0/pipelineprobe/__init__.py +5 -0
- pipelineprobe-0.3.0/pipelineprobe/cli.py +702 -0
- pipelineprobe-0.3.0/pipelineprobe/config.py +111 -0
- pipelineprobe-0.3.0/pipelineprobe/connectors/__init__.py +1 -0
- pipelineprobe-0.3.0/pipelineprobe/connectors/airflow.py +229 -0
- pipelineprobe-0.3.0/pipelineprobe/connectors/bigquery.py +118 -0
- pipelineprobe-0.3.0/pipelineprobe/connectors/dagster.py +188 -0
- pipelineprobe-0.3.0/pipelineprobe/connectors/dbt.py +68 -0
- pipelineprobe-0.3.0/pipelineprobe/connectors/postgres.py +42 -0
- pipelineprobe-0.3.0/pipelineprobe/connectors/prefect.py +153 -0
- pipelineprobe-0.3.0/pipelineprobe/connectors/snowflake.py +143 -0
- pipelineprobe-0.3.0/pipelineprobe/models.py +44 -0
- pipelineprobe-0.3.0/pipelineprobe/renderer.py +63 -0
- pipelineprobe-0.3.0/pipelineprobe/rules/__init__.py +19 -0
- pipelineprobe-0.3.0/pipelineprobe/rules/airflow_rules.py +153 -0
- pipelineprobe-0.3.0/pipelineprobe/rules/cost_rules.py +151 -0
- pipelineprobe-0.3.0/pipelineprobe/rules/dbt_rules.py +44 -0
- pipelineprobe-0.3.0/pipelineprobe/rules/engine.py +25 -0
- pipelineprobe-0.3.0/pipelineprobe/rules/postgres_rules.py +49 -0
- pipelineprobe-0.3.0/pipelineprobe/templates/report.html +839 -0
- pipelineprobe-0.3.0/pyproject.toml +52 -0
- pipelineprobe-0.3.0/scripts/build_demo_report.py +138 -0
- pipelineprobe-0.3.0/site/assets/poster.jpg +0 -0
- pipelineprobe-0.3.0/site/demo/sample_audit.json +90 -0
- pipelineprobe-0.3.0/site/index.html +1926 -0
- pipelineprobe-0.3.0/site/report/index.html +1009 -0
- pipelineprobe-0.3.0/tests/conftest.py +86 -0
- pipelineprobe-0.3.0/tests/test_cli.py +101 -0
- pipelineprobe-0.3.0/tests/test_config.py +13 -0
- pipelineprobe-0.3.0/tests/test_connectors.py +233 -0
- pipelineprobe-0.3.0/tests/test_cost_rules.py +210 -0
- pipelineprobe-0.3.0/tests/test_demo_report.py +76 -0
- pipelineprobe-0.3.0/tests/test_new_connectors.py +312 -0
- pipelineprobe-0.3.0/tests/test_optional_cloud_deps.py +178 -0
- pipelineprobe-0.3.0/tests/test_renderer.py +70 -0
- pipelineprobe-0.3.0/tests/test_rules.py +210 -0
- pipelineprobe-0.3.0/tests/test_site_landing.py +87 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Version control
|
|
2
|
+
.git
|
|
3
|
+
.gitignore
|
|
4
|
+
|
|
5
|
+
# Environments
|
|
6
|
+
.venv
|
|
7
|
+
venv
|
|
8
|
+
|
|
9
|
+
# Python bytecode and caches
|
|
10
|
+
__pycache__
|
|
11
|
+
*.py[cod]
|
|
12
|
+
*.egg-info
|
|
13
|
+
.ruff_cache
|
|
14
|
+
.pytest_cache
|
|
15
|
+
|
|
16
|
+
# Build artifacts
|
|
17
|
+
dist
|
|
18
|
+
build
|
|
19
|
+
|
|
20
|
+
# Generated reports
|
|
21
|
+
reports
|
|
22
|
+
|
|
23
|
+
# Internal / non-runtime directories
|
|
24
|
+
.superpowers
|
|
25
|
+
video-work
|
|
26
|
+
site
|
|
27
|
+
|
|
28
|
+
# Tests, docs and lockfile are not needed to build the image
|
|
29
|
+
tests
|
|
30
|
+
docs
|
|
31
|
+
uv.lock
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ["main"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test-and-lint:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.11", "3.12"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: |
|
|
26
|
+
python -m pip install --upgrade pip
|
|
27
|
+
pip install -e ".[dev,cloud]"
|
|
28
|
+
|
|
29
|
+
- name: Lint with ruff
|
|
30
|
+
run: ruff check .
|
|
31
|
+
|
|
32
|
+
- name: Run tests
|
|
33
|
+
run: pytest
|
|
34
|
+
|
|
35
|
+
build:
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
|
|
40
|
+
- name: Set up Python
|
|
41
|
+
uses: actions/setup-python@v5
|
|
42
|
+
with:
|
|
43
|
+
python-version: "3.11"
|
|
44
|
+
|
|
45
|
+
- name: Build sdist and wheel
|
|
46
|
+
run: |
|
|
47
|
+
python -m pip install --upgrade pip build
|
|
48
|
+
python -m build
|
|
49
|
+
|
|
50
|
+
- name: Install wheel in a clean venv
|
|
51
|
+
run: |
|
|
52
|
+
python -m venv /tmp/pipelineprobe-venv
|
|
53
|
+
/tmp/pipelineprobe-venv/bin/python -m pip install --upgrade pip
|
|
54
|
+
/tmp/pipelineprobe-venv/bin/pip install dist/*.whl
|
|
55
|
+
/tmp/pipelineprobe-venv/bin/pipelineprobe --version
|
|
56
|
+
|
|
57
|
+
docker:
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v4
|
|
61
|
+
|
|
62
|
+
- name: Build Docker image
|
|
63
|
+
run: docker build -t pipelineprobe:test .
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Pages
|
|
2
|
+
|
|
3
|
+
# Pages hosting is deferred: the site/ directory is published manually with
|
|
4
|
+
# "Run workflow" once Pages is enabled in the repository settings
|
|
5
|
+
# (Settings -> Pages -> Source: GitHub Actions). Publishing on every push
|
|
6
|
+
# would fail until that setting exists.
|
|
7
|
+
on:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
pages: write
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
15
|
+
concurrency:
|
|
16
|
+
group: pages
|
|
17
|
+
cancel-in-progress: false
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
deploy:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
environment:
|
|
23
|
+
name: github-pages
|
|
24
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
25
|
+
steps:
|
|
26
|
+
- name: Checkout
|
|
27
|
+
uses: actions/checkout@v4
|
|
28
|
+
|
|
29
|
+
- name: Fetch demo video (optional)
|
|
30
|
+
run: |
|
|
31
|
+
mkdir -p site/assets
|
|
32
|
+
curl -fL --retry 3 --retry-delay 2 \
|
|
33
|
+
https://github.com/willowvibe/pipelineprobe/releases/download/v0.3.0/pipelineprobe-demo.mp4 \
|
|
34
|
+
-o site/assets/pipelineprobe-demo.mp4 \
|
|
35
|
+
|| {
|
|
36
|
+
rm -f site/assets/pipelineprobe-demo.mp4
|
|
37
|
+
echo "::notice::pipelineprobe-demo.mp4 is not published yet; deploying the page with its poster and release fallback."
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
- name: Configure Pages
|
|
41
|
+
uses: actions/configure-pages@v5
|
|
42
|
+
|
|
43
|
+
- name: Upload Pages artifact
|
|
44
|
+
uses: actions/upload-pages-artifact@v3
|
|
45
|
+
with:
|
|
46
|
+
path: site
|
|
47
|
+
|
|
48
|
+
- name: Deploy to GitHub Pages
|
|
49
|
+
id: deployment
|
|
50
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
release:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Set up Python
|
|
18
|
+
uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.11"
|
|
21
|
+
|
|
22
|
+
- name: Build sdist and wheel
|
|
23
|
+
run: |
|
|
24
|
+
python -m pip install --upgrade pip build
|
|
25
|
+
python -m build
|
|
26
|
+
|
|
27
|
+
- name: Create GitHub release
|
|
28
|
+
env:
|
|
29
|
+
GH_TOKEN: ${{ github.token }}
|
|
30
|
+
run: gh release create "$GITHUB_REF_NAME" dist/* --generate-notes
|
|
31
|
+
|
|
32
|
+
publish-pypi:
|
|
33
|
+
# Trusted Publishing: PyPI verifies this workflow through GitHub OIDC, so
|
|
34
|
+
# no API token is stored. The pending publisher on PyPI must name this
|
|
35
|
+
# repository, the file `release.yml`, and the environment `pypi`.
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
environment:
|
|
38
|
+
name: pypi
|
|
39
|
+
url: https://pypi.org/p/pipelineprobe
|
|
40
|
+
permissions:
|
|
41
|
+
contents: read
|
|
42
|
+
id-token: write
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/checkout@v4
|
|
45
|
+
|
|
46
|
+
- name: Set up Python
|
|
47
|
+
uses: actions/setup-python@v5
|
|
48
|
+
with:
|
|
49
|
+
python-version: "3.11"
|
|
50
|
+
|
|
51
|
+
- name: Build sdist and wheel
|
|
52
|
+
run: |
|
|
53
|
+
python -m pip install --upgrade pip build
|
|
54
|
+
python -m build
|
|
55
|
+
|
|
56
|
+
- name: Publish to PyPI
|
|
57
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
58
|
+
with:
|
|
59
|
+
packages-dir: dist/
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py,cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
/target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
#Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# poetry
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
102
|
+
#poetry.lock
|
|
103
|
+
|
|
104
|
+
# pdm
|
|
105
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
106
|
+
#pdm.lock
|
|
107
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
108
|
+
# in version control.
|
|
109
|
+
# https://pdm.fming.dev/#use-with-ide
|
|
110
|
+
.pdm.toml
|
|
111
|
+
|
|
112
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
113
|
+
__pypackages__/
|
|
114
|
+
|
|
115
|
+
# Celery stuff
|
|
116
|
+
celerybeat-schedule
|
|
117
|
+
celerybeat.pid
|
|
118
|
+
|
|
119
|
+
# SageMath parsed files
|
|
120
|
+
*.sage.py
|
|
121
|
+
|
|
122
|
+
# Environments
|
|
123
|
+
.env
|
|
124
|
+
.venv
|
|
125
|
+
env/
|
|
126
|
+
venv/
|
|
127
|
+
ENV/
|
|
128
|
+
env.bak/
|
|
129
|
+
venv.bak/
|
|
130
|
+
|
|
131
|
+
# Spyder project settings
|
|
132
|
+
.spyderproject
|
|
133
|
+
.spyproject
|
|
134
|
+
|
|
135
|
+
# Rope project settings
|
|
136
|
+
.ropeproject
|
|
137
|
+
|
|
138
|
+
# mkdocs documentation
|
|
139
|
+
|
|
140
|
+
# mypy
|
|
141
|
+
.mypy_cache/
|
|
142
|
+
.dmypy.json
|
|
143
|
+
dmypy.json
|
|
144
|
+
|
|
145
|
+
# Pyre type checker
|
|
146
|
+
.pyre/
|
|
147
|
+
|
|
148
|
+
# pytype static type analyzer
|
|
149
|
+
.pytype/
|
|
150
|
+
|
|
151
|
+
# Cython debug symbols
|
|
152
|
+
cython_debug/
|
|
153
|
+
|
|
154
|
+
# PyCharm
|
|
155
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
156
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
157
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
158
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
159
|
+
#.idea/
|
|
160
|
+
|
|
161
|
+
# Mac OS
|
|
162
|
+
.DS_Store
|
|
163
|
+
|
|
164
|
+
# PipelineProbe specific
|
|
165
|
+
/reports/
|
|
166
|
+
examples/quickstart/reports/*
|
|
167
|
+
!examples/quickstart/reports/.gitkeep
|
|
168
|
+
/pipelineprobe.yml
|
|
169
|
+
uv.lock
|
|
170
|
+
video-work/
|
|
171
|
+
site/assets/*.mp4
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to PipelineProbe are documented here.
|
|
4
|
+
|
|
5
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
6
|
+
Versioning follows [Semantic Versioning](https://semver.org/).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## [0.3.0] — 2026-11-15
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
#### v0.3.0 — Quickstart, sample report, and install shape
|
|
15
|
+
- **Docker quickstart** (`examples/quickstart/`) — a real Airflow 2.7.1, dbt and
|
|
16
|
+
Postgres stack with 8 seeded DAGs and deterministic failures;
|
|
17
|
+
`docker compose up --build` writes HTML and JSON reports with no cloud
|
|
18
|
+
accounts or existing pipelines required.
|
|
19
|
+
- **Sample report** (`site/report/index.html`) — the quickstart audit committed
|
|
20
|
+
as one self-contained HTML file with working severity filters.
|
|
21
|
+
- **Optional `cloud` extra** — `google-cloud-bigquery` and
|
|
22
|
+
`snowflake-connector-python` moved out of the default install. The base
|
|
23
|
+
package covers Airflow, dbt and Postgres; configuring a BigQuery or Snowflake
|
|
24
|
+
warehouse without the extra exits with an install hint instead of a
|
|
25
|
+
traceback.
|
|
26
|
+
|
|
27
|
+
#### v0.2.0 — New Orchestrator Connectors
|
|
28
|
+
- **Prefect connector** (`pipelineprobe/connectors/prefect.py`) — connects to a
|
|
29
|
+
Prefect 2/3 Server or Cloud workspace via the Prefect REST API. Maps flows
|
|
30
|
+
and flow runs to the shared `Dag` / `DagRun` models so all existing rules
|
|
31
|
+
(high failure rate, stale pipelines, …) work out of the box. Prefect Cloud
|
|
32
|
+
authentication is handled via `orchestrator.api_key` in config.
|
|
33
|
+
- **Dagster connector** (`pipelineprobe/connectors/dagster.py`) — queries the
|
|
34
|
+
Dagster GraphQL API to fetch runs, groups them by job name, and exposes each
|
|
35
|
+
job as a `Dag` object. Supports Dagster Cloud via `orchestrator.api_key`.
|
|
36
|
+
- `orchestrator.type` config field now supports `"airflow"` (default),
|
|
37
|
+
`"prefect"`, and `"dagster"`. The `doctor` command probes all three.
|
|
38
|
+
- `orchestrator.api_key` config field (+ `PIPELINEPROBE_ORCHESTRATOR_API_KEY`
|
|
39
|
+
env var) for Prefect Cloud / Dagster Cloud bearer-token authentication.
|
|
40
|
+
- `orchestrator_type` key added to the `summary.metadata` block in JSON/HTML
|
|
41
|
+
reports so downstream tooling knows which orchestrator was audited.
|
|
42
|
+
|
|
43
|
+
#### v0.3.0 — Cost Insights
|
|
44
|
+
- **BigQuery cost insights** — `BigQueryConnector.get_cost_insights_sync()`
|
|
45
|
+
queries `INFORMATION_SCHEMA.JOBS_BY_PROJECT` (last 30 days) and returns the
|
|
46
|
+
25 tables with the highest bytes billed.
|
|
47
|
+
- **Snowflake credit insights** — `SnowflakeConnector.get_cost_insights_sync()`
|
|
48
|
+
queries `SNOWFLAKE.ACCOUNT_USAGE.WAREHOUSE_METERING_HISTORY` (last 30 days)
|
|
49
|
+
and returns credit consumption per virtual warehouse.
|
|
50
|
+
- **`check_expensive_bq_tables`** rule — warning at ≥ 500 GiB billed / 30 days,
|
|
51
|
+
critical at ≥ 5 TiB.
|
|
52
|
+
- **`check_snowflake_credit_spenders`** rule — warning at ≥ 500 credits / 30 days,
|
|
53
|
+
critical at ≥ 5 000 credits.
|
|
54
|
+
- `report.include_cost_section: true` config flag enables cost-insight queries
|
|
55
|
+
during `pipelineprobe audit`; defaults to `false` (opt-in to avoid requiring
|
|
56
|
+
elevated IAM permissions by default).
|
|
57
|
+
- `warehouse.bq_region` config field allows overriding the default BigQuery
|
|
58
|
+
multi-region (`region-us`) for teams using e.g. `region-eu`.
|
|
59
|
+
- `doctor` command extended to probe cost-insight permissions when
|
|
60
|
+
`include_cost_section: true` is set.
|
|
61
|
+
|
|
62
|
+
#### General
|
|
63
|
+
- `pipelineprobe audit` now prints a one-line health summary
|
|
64
|
+
(`Health score: N/100 — X critical, Y warning, Z info`) before the
|
|
65
|
+
fail-threshold check.
|
|
66
|
+
- `SUPPORTED_ORCHESTRATORS` constant exported from `pipelineprobe.config` for
|
|
67
|
+
validation use in the CLI.
|
|
68
|
+
- `init` command now generates a config template documenting all new fields.
|
|
69
|
+
|
|
70
|
+
### Added (previous Unreleased — UI / bug fixes)
|
|
71
|
+
- **Report UI overhaul** — sticky topbar, animated SVG health-score ring, severity filter buttons (All / Critical / Warnings / Info), affected-resource tags on each finding card, subtle per-severity background tints, and responsive print styles.
|
|
72
|
+
- **Info count card** — the summary grid now shows a fourth card for informational findings alongside Critical and Warnings.
|
|
73
|
+
- `info_count` included in the JSON report summary for downstream tooling.
|
|
74
|
+
- `ring_offset` computed in the renderer and injected into the Jinja2 template so the SVG ring accurately reflects the health score.
|
|
75
|
+
- BigQuery connector querying `INFORMATION_SCHEMA.TABLE_STORAGE` and `COLUMNS` for real `has_timestamps` detection.
|
|
76
|
+
- Snowflake connector using a CTE pattern to work around correlated subquery restrictions.
|
|
77
|
+
- `python-dateutil` declared as an explicit dependency in `pyproject.toml`.
|
|
78
|
+
|
|
79
|
+
### Fixed
|
|
80
|
+
- `check_missing_slas` no longer reports false "no SLA configured" findings when the orchestrator API does not expose task SLA configuration (Airflow 2.7.1 never serializes `sla`).
|
|
81
|
+
- `pipelineprobe audit --format` was shadowing Python's built-in `format()`. Renamed CLI parameter to `report_format` internally.
|
|
82
|
+
- Added validation to `--format` — passing unsupported values (e.g. `csv`) now exits with a clear error.
|
|
83
|
+
- `get_dags()` in the Airflow connector now paginates correctly. Previously, organisations with more than 100 DAGs would silently receive truncated results.
|
|
84
|
+
- `check_stale_dags` now correctly flags active DAGs that have **zero recorded runs** (previously silently skipped).
|
|
85
|
+
- Double-space indentation bug in `check_stale_dags` fixed.
|
|
86
|
+
- `BigQueryConnector` was returning `has_timestamps = True` for every table (hardcoded). Now queries actual `INFORMATION_SCHEMA.COLUMNS`.
|
|
87
|
+
- `SnowflakeConnector` was silently connecting with empty string credentials. Now validates and returns early with a log error.
|
|
88
|
+
- `SnowflakeConnector` correlated subquery replaced with a CTE, which is supported by Snowflake's `INFORMATION_SCHEMA`.
|
|
89
|
+
- `affected_resources` in warehouse rules could contain `None` when `tablename` was absent. Now falls back to `"<unknown>"`.
|
|
90
|
+
- `renderer.render_json()` raised `TypeError` when `Task.sla` (`timedelta`) was serialized. Added `_json_default` handler.
|
|
91
|
+
- `test_cli_init` was failing if a `pipelineprobe.yml` already existed in the project root. Test now uses a temporary directory.
|
|
92
|
+
- All bare `print()` error statements replaced with `logging.getLogger(__name__)` across all modules.
|
|
93
|
+
- PostgreSQL connector now uses `try/finally` to guarantee `conn.close()` is called even on errors.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 0.1.0 — 2026-03-15
|
|
98
|
+
|
|
99
|
+
### Added
|
|
100
|
+
- **Phase 0** — Project skeleton, `pyproject.toml`, CI workflow (ruff + pytest), CLI entry point.
|
|
101
|
+
- **Phase 1 — MVP connectors:**
|
|
102
|
+
- `AirflowConnector` — fetches DAGs, DAG runs, and task configurations via the Airflow REST API.
|
|
103
|
+
- `DbtConnector` — reads `manifest.json` and `run_results.json`; counts tests per model.
|
|
104
|
+
- `PostgresConnector` — queries `pg_stat_user_tables` and `information_schema.columns`.
|
|
105
|
+
- **Phase 1 — Rules Engine:**
|
|
106
|
+
- `check_missing_retries` — warns on tasks with no retry configuration.
|
|
107
|
+
- `check_missing_slas` — informs on tasks with no SLA.
|
|
108
|
+
- `check_high_failure_rate` — critical alert for DAGs with >20% failure rate over ≥5 runs.
|
|
109
|
+
- `check_stale_dags` — warns on active DAGs with no successful run in 7+ days.
|
|
110
|
+
- `check_missing_tests` — warns on dbt models with zero tests.
|
|
111
|
+
- `check_failing_models` — critical alert for dbt models that failed their last run.
|
|
112
|
+
- `check_large_tables` — warns on tables with >10M rows.
|
|
113
|
+
- `check_missing_timestamps` — warns on tables >1M rows without `created_at`/`updated_at`.
|
|
114
|
+
- **Phase 1 — Report Renderer** — Jinja2 HTML template; JSON output via `model_dump()`.
|
|
115
|
+
- **Phase 2 — DX & CI:**
|
|
116
|
+
- `pipelineprobe init` command generating a default `pipelineprobe.yml`.
|
|
117
|
+
- `--fail-on-critical` and `--format` CLI overrides.
|
|
118
|
+
- `examples/github-actions/pipelineprobe.yml` GitHub Actions workflow.
|
|
119
|
+
- `docs/ci-integration.md`.
|
|
120
|
+
- `.gitignore` with standard Python exclusions.
|
|
121
|
+
- **Phase 3 — Extended Integrations:**
|
|
122
|
+
- `BigQueryConnector`.
|
|
123
|
+
- `SnowflakeConnector`.
|
|
124
|
+
- `pipelineprobe doctor` command for connectivity validation.
|
|
125
|
+
- `pipelineprobe diff` command for regression detection between two JSON reports.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
[Unreleased]: https://github.com/willowvibe/pipelineprobe/compare/v0.3.0...HEAD
|
|
130
|
+
[0.3.0]: https://github.com/willowvibe/pipelineprobe/releases/tag/v0.3.0
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Contributing to PipelineProbe
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing! PipelineProbe is an open-source project by [WillowVibe](https://www.willowvibe.com) and we welcome contributions of all kinds — bug reports, documentation improvements, new rules, and new connectors.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Development Setup
|
|
8
|
+
|
|
9
|
+
### Prerequisites
|
|
10
|
+
|
|
11
|
+
- Python 3.11+
|
|
12
|
+
- `pip`
|
|
13
|
+
- A virtual environment tool (`venv`, `hatch`, `pyenv`, etc.)
|
|
14
|
+
|
|
15
|
+
### Clone and Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
git clone https://github.com/willowvibe/pipelineprobe.git
|
|
19
|
+
cd pipelineprobe
|
|
20
|
+
|
|
21
|
+
python -m venv .venv
|
|
22
|
+
source .venv/bin/activate # Linux / macOS
|
|
23
|
+
# .venv\Scripts\activate # Windows
|
|
24
|
+
|
|
25
|
+
pip install -e ".[dev]"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Running Tests
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pytest
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Tests live in `tests/` and use `pytest`. Fixtures for mock data are in `tests/conftest.py`.
|
|
37
|
+
|
|
38
|
+
To run a specific file or test:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pytest tests/test_rules.py
|
|
42
|
+
pytest tests/test_renderer.py::test_render_html_no_issues
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Linting & Formatting
|
|
48
|
+
|
|
49
|
+
We use [ruff](https://docs.astral.sh/ruff/) for both linting and formatting:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
ruff check . # report lint errors
|
|
53
|
+
ruff check --fix . # auto-fix safe issues
|
|
54
|
+
ruff format . # format all files
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
CI fails if `ruff check .` reports any errors. Run both before opening a PR.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Project Structure
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
pipelineprobe/
|
|
65
|
+
├── pipelineprobe/
|
|
66
|
+
│ ├── cli.py # CLI entry points (audit, init, doctor, diff)
|
|
67
|
+
│ ├── config.py # YAML + env var config loader (Pydantic)
|
|
68
|
+
│ ├── models.py # Core domain models (Dag, Task, DbtModel, Issue)
|
|
69
|
+
│ ├── renderer.py # HTML + JSON report renderer (Jinja2)
|
|
70
|
+
│ ├── connectors/
|
|
71
|
+
│ │ ├── airflow.py # Apache Airflow REST API connector
|
|
72
|
+
│ │ ├── dbt.py # dbt manifest / run_results reader
|
|
73
|
+
│ │ ├── postgres.py # PostgreSQL INFORMATION_SCHEMA queries
|
|
74
|
+
│ │ ├── bigquery.py # BigQuery INFORMATION_SCHEMA queries
|
|
75
|
+
│ │ └── snowflake.py # Snowflake INFORMATION_SCHEMA queries
|
|
76
|
+
│ ├── rules/
|
|
77
|
+
│ │ ├── engine.py # Pluggable rule registration and execution
|
|
78
|
+
│ │ ├── airflow_rules.py
|
|
79
|
+
│ │ ├── dbt_rules.py
|
|
80
|
+
│ │ └── postgres_rules.py
|
|
81
|
+
│ └── templates/
|
|
82
|
+
│ └── report.html # Jinja2 HTML report template
|
|
83
|
+
├── tests/
|
|
84
|
+
│ ├── conftest.py # Shared pytest fixtures
|
|
85
|
+
│ ├── test_cli.py
|
|
86
|
+
│ ├── test_config.py
|
|
87
|
+
│ ├── test_connectors.py
|
|
88
|
+
│ ├── test_renderer.py
|
|
89
|
+
│ └── test_rules.py
|
|
90
|
+
├── docs/
|
|
91
|
+
├── examples/
|
|
92
|
+
└── pyproject.toml
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Adding a New Rule
|
|
98
|
+
|
|
99
|
+
1. Add a function to the appropriate `rules/` module (e.g. `airflow_rules.py`):
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from typing import List
|
|
103
|
+
from pipelineprobe.models import Issue
|
|
104
|
+
|
|
105
|
+
def check_my_new_rule(context: dict) -> List[Issue]:
|
|
106
|
+
issues = []
|
|
107
|
+
for dag in context.get("airflow_dags", []):
|
|
108
|
+
if some_condition(dag):
|
|
109
|
+
issues.append(Issue(
|
|
110
|
+
severity="warning",
|
|
111
|
+
category="dag",
|
|
112
|
+
summary="Short description of the problem",
|
|
113
|
+
details="Longer explanation with context.",
|
|
114
|
+
recommendation="Concrete steps to fix it.",
|
|
115
|
+
affected_resources=[dag.id],
|
|
116
|
+
))
|
|
117
|
+
return issues
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
2. Register it at the bottom of the same file:
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
def register_airflow_rules(engine):
|
|
124
|
+
...
|
|
125
|
+
engine.register_rule(check_my_new_rule)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
3. Write tests in `tests/test_rules.py` — at minimum one happy-path and one edge-case test.
|
|
129
|
+
|
|
130
|
+
4. If the rule is configurable (e.g. severity override), document it in `docs/configuration.md` under the `rules.severity_overrides` table.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Adding a New Connector
|
|
135
|
+
|
|
136
|
+
1. Create `pipelineprobe/connectors/my_connector.py`.
|
|
137
|
+
2. Implement a class with a `get_stats_sync() -> List[Dict[str, Any]]` method returning rows with at least: `schemaname`, `tablename`, `row_count`, `has_timestamps`.
|
|
138
|
+
3. Route `cfg.warehouse.type == "my_connector"` in the `audit()` function in `cli.py`.
|
|
139
|
+
4. Add any required credentials to `WarehouseConfig` in `config.py`.
|
|
140
|
+
5. Document the new connector and its config fields in `docs/configuration.md`.
|
|
141
|
+
6. Add at least one connectivity test in `tests/test_connectors.py`.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Working on the HTML Report
|
|
146
|
+
|
|
147
|
+
The report template lives at `pipelineprobe/templates/report.html` and is rendered by `renderer.py` using Jinja2.
|
|
148
|
+
|
|
149
|
+
Variables available in the template:
|
|
150
|
+
|
|
151
|
+
| Variable | Type | Description |
|
|
152
|
+
|---|---|---|
|
|
153
|
+
| `issues` | `List[Issue]` | All findings from the rule engine |
|
|
154
|
+
| `summary` | `dict` | Health score, counts, dag_count, metadata |
|
|
155
|
+
| `top_actions` | `List[Issue]` | Top 3 critical/warning issues |
|
|
156
|
+
| `metadata` | `dict` | orchestrator_url, warehouse_type, dbt_target |
|
|
157
|
+
| `generated_at` | `str` | Formatted timestamp string |
|
|
158
|
+
| `version` | `str` | PipelineProbe version |
|
|
159
|
+
| `ring_offset` | `float` | SVG ring stroke-dashoffset (0 = full, 251.3 = empty) |
|
|
160
|
+
|
|
161
|
+
To preview a rendered report, run a real or mock audit and open `./reports/pipelineprobe-report.html` in a browser.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Pull Request Process
|
|
166
|
+
|
|
167
|
+
1. Fork the repo and create your branch from `main`.
|
|
168
|
+
2. Write tests for your changes.
|
|
169
|
+
3. Ensure `ruff check .` and `pytest` both pass.
|
|
170
|
+
4. Open a PR with a description covering:
|
|
171
|
+
- **What** changed
|
|
172
|
+
- **Why** it is needed
|
|
173
|
+
- **How** to test it manually
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Reporting Bugs
|
|
178
|
+
|
|
179
|
+
Open a GitHub Issue with:
|
|
180
|
+
- PipelineProbe version (`pip show pipelineprobe`)
|
|
181
|
+
- Python version
|
|
182
|
+
- A clear description of what happened vs. what you expected
|
|
183
|
+
- Sanitized config (no credentials) and relevant log output
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Code of Conduct
|
|
188
|
+
|
|
189
|
+
Be kind, constructive, and respectful. We follow the [Contributor Covenant](https://www.contributor-covenant.org/) Code of Conduct.
|