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.
Files changed (68) hide show
  1. pipelineprobe-0.3.0/.dockerignore +31 -0
  2. pipelineprobe-0.3.0/.github/workflows/ci.yml +63 -0
  3. pipelineprobe-0.3.0/.github/workflows/pages.yml +50 -0
  4. pipelineprobe-0.3.0/.github/workflows/release.yml +59 -0
  5. pipelineprobe-0.3.0/.gitignore +171 -0
  6. pipelineprobe-0.3.0/.vscode/settings.json +1 -0
  7. pipelineprobe-0.3.0/CHANGELOG.md +130 -0
  8. pipelineprobe-0.3.0/CONTRIBUTING.md +189 -0
  9. pipelineprobe-0.3.0/Dockerfile +18 -0
  10. pipelineprobe-0.3.0/LICENSE +21 -0
  11. pipelineprobe-0.3.0/PKG-INFO +148 -0
  12. pipelineprobe-0.3.0/README.md +122 -0
  13. pipelineprobe-0.3.0/docs/architecture.md +194 -0
  14. pipelineprobe-0.3.0/docs/assets/pipelineprobe-demo.gif +0 -0
  15. pipelineprobe-0.3.0/docs/bugs_improvements.md +65 -0
  16. pipelineprobe-0.3.0/docs/ci-integration.md +155 -0
  17. pipelineprobe-0.3.0/docs/configuration.md +217 -0
  18. pipelineprobe-0.3.0/docs/report-screenshot.png +0 -0
  19. pipelineprobe-0.3.0/docs/requirement.md +403 -0
  20. pipelineprobe-0.3.0/docs/superpowers/plans/2026-11-15-pipelineprobe-v0.3.0.md +118 -0
  21. pipelineprobe-0.3.0/examples/github-actions/pipelineprobe.yml +40 -0
  22. pipelineprobe-0.3.0/examples/quickstart/README.md +128 -0
  23. pipelineprobe-0.3.0/examples/quickstart/dags/demo_pipelines.py +60 -0
  24. pipelineprobe-0.3.0/examples/quickstart/dbt_project/dbt_project.yml +28 -0
  25. pipelineprobe-0.3.0/examples/quickstart/dbt_project/target/manifest.json +313 -0
  26. pipelineprobe-0.3.0/examples/quickstart/dbt_project/target/run_results.json +124 -0
  27. pipelineprobe-0.3.0/examples/quickstart/docker-compose.yml +114 -0
  28. pipelineprobe-0.3.0/examples/quickstart/init-warehouse.sql +96 -0
  29. pipelineprobe-0.3.0/examples/quickstart/pipelineprobe.yml +27 -0
  30. pipelineprobe-0.3.0/examples/quickstart/reports/.gitkeep +0 -0
  31. pipelineprobe-0.3.0/examples/quickstart/run-audit.sh +304 -0
  32. pipelineprobe-0.3.0/pipelineprobe/__init__.py +5 -0
  33. pipelineprobe-0.3.0/pipelineprobe/cli.py +702 -0
  34. pipelineprobe-0.3.0/pipelineprobe/config.py +111 -0
  35. pipelineprobe-0.3.0/pipelineprobe/connectors/__init__.py +1 -0
  36. pipelineprobe-0.3.0/pipelineprobe/connectors/airflow.py +229 -0
  37. pipelineprobe-0.3.0/pipelineprobe/connectors/bigquery.py +118 -0
  38. pipelineprobe-0.3.0/pipelineprobe/connectors/dagster.py +188 -0
  39. pipelineprobe-0.3.0/pipelineprobe/connectors/dbt.py +68 -0
  40. pipelineprobe-0.3.0/pipelineprobe/connectors/postgres.py +42 -0
  41. pipelineprobe-0.3.0/pipelineprobe/connectors/prefect.py +153 -0
  42. pipelineprobe-0.3.0/pipelineprobe/connectors/snowflake.py +143 -0
  43. pipelineprobe-0.3.0/pipelineprobe/models.py +44 -0
  44. pipelineprobe-0.3.0/pipelineprobe/renderer.py +63 -0
  45. pipelineprobe-0.3.0/pipelineprobe/rules/__init__.py +19 -0
  46. pipelineprobe-0.3.0/pipelineprobe/rules/airflow_rules.py +153 -0
  47. pipelineprobe-0.3.0/pipelineprobe/rules/cost_rules.py +151 -0
  48. pipelineprobe-0.3.0/pipelineprobe/rules/dbt_rules.py +44 -0
  49. pipelineprobe-0.3.0/pipelineprobe/rules/engine.py +25 -0
  50. pipelineprobe-0.3.0/pipelineprobe/rules/postgres_rules.py +49 -0
  51. pipelineprobe-0.3.0/pipelineprobe/templates/report.html +839 -0
  52. pipelineprobe-0.3.0/pyproject.toml +52 -0
  53. pipelineprobe-0.3.0/scripts/build_demo_report.py +138 -0
  54. pipelineprobe-0.3.0/site/assets/poster.jpg +0 -0
  55. pipelineprobe-0.3.0/site/demo/sample_audit.json +90 -0
  56. pipelineprobe-0.3.0/site/index.html +1926 -0
  57. pipelineprobe-0.3.0/site/report/index.html +1009 -0
  58. pipelineprobe-0.3.0/tests/conftest.py +86 -0
  59. pipelineprobe-0.3.0/tests/test_cli.py +101 -0
  60. pipelineprobe-0.3.0/tests/test_config.py +13 -0
  61. pipelineprobe-0.3.0/tests/test_connectors.py +233 -0
  62. pipelineprobe-0.3.0/tests/test_cost_rules.py +210 -0
  63. pipelineprobe-0.3.0/tests/test_demo_report.py +76 -0
  64. pipelineprobe-0.3.0/tests/test_new_connectors.py +312 -0
  65. pipelineprobe-0.3.0/tests/test_optional_cloud_deps.py +178 -0
  66. pipelineprobe-0.3.0/tests/test_renderer.py +70 -0
  67. pipelineprobe-0.3.0/tests/test_rules.py +210 -0
  68. 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.