specdbt 0.1.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.
- specdbt-0.1.0/.claude/skills/specdbt/SKILL.md +25 -0
- specdbt-0.1.0/.github/CODEOWNERS +1 -0
- specdbt-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +29 -0
- specdbt-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- specdbt-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +14 -0
- specdbt-0.1.0/.github/dependabot.yml +20 -0
- specdbt-0.1.0/.github/pull_request_template.md +7 -0
- specdbt-0.1.0/.github/workflows/ci.yml +100 -0
- specdbt-0.1.0/.github/workflows/release.yml +166 -0
- specdbt-0.1.0/.gitignore +30 -0
- specdbt-0.1.0/.pre-commit-config.yaml +48 -0
- specdbt-0.1.0/AGENTS.md +20 -0
- specdbt-0.1.0/CLAUDE.md +5 -0
- specdbt-0.1.0/CONTRIBUTING.md +71 -0
- specdbt-0.1.0/Justfile +90 -0
- specdbt-0.1.0/LICENSE +21 -0
- specdbt-0.1.0/PKG-INFO +95 -0
- specdbt-0.1.0/README.md +65 -0
- specdbt-0.1.0/SECURITY.md +13 -0
- specdbt-0.1.0/docker-compose.yml +6 -0
- specdbt-0.1.0/docs/knowledge/adapters.md +37 -0
- specdbt-0.1.0/docs/knowledge/ai.md +19 -0
- specdbt-0.1.0/docs/knowledge/cli.md +19 -0
- specdbt-0.1.0/docs/knowledge/databricks-validation-checklist.md +42 -0
- specdbt-0.1.0/docs/knowledge/dbt-integration.md +42 -0
- specdbt-0.1.0/docs/knowledge/gherkin-style-guide.md +82 -0
- specdbt-0.1.0/docs/knowledge/index.md +27 -0
- specdbt-0.1.0/docs/knowledge/native-unit-tests.md +38 -0
- specdbt-0.1.0/docs/knowledge/pipeline.md +55 -0
- specdbt-0.1.0/docs/knowledge/two-tier-design.md +64 -0
- specdbt-0.1.0/examples/jaffle_shop/dbt_project.yml +23 -0
- specdbt-0.1.0/examples/jaffle_shop/features/macros/bucket_order_value/bucket_order_value.feature +36 -0
- specdbt-0.1.0/examples/jaffle_shop/features/macros/generate_surrogate_key/generate_surrogate_key.feature +12 -0
- specdbt-0.1.0/examples/jaffle_shop/features/macros/generate_surrogate_key/surrogate_key_quality.feature +24 -0
- specdbt-0.1.0/examples/jaffle_shop/features/macros/order_value_summary/order_value_summary.feature +15 -0
- specdbt-0.1.0/examples/jaffle_shop/features/macros/pivot_sum/pivot_sum.feature +16 -0
- specdbt-0.1.0/examples/jaffle_shop/features/macros/star/star.feature +12 -0
- specdbt-0.1.0/examples/jaffle_shop/features/models/customers/customers.feature +19 -0
- specdbt-0.1.0/examples/jaffle_shop/features/models/order_history/order_history.feature +41 -0
- specdbt-0.1.0/examples/jaffle_shop/features/models/order_surrogate_keys/order_surrogate_keys.feature +18 -0
- specdbt-0.1.0/examples/jaffle_shop/features/models/stg_customers/stg_customers.feature +11 -0
- specdbt-0.1.0/examples/jaffle_shop/macros/bucket_order_value.sql +7 -0
- specdbt-0.1.0/examples/jaffle_shop/macros/order_value_summary.sql +7 -0
- specdbt-0.1.0/examples/jaffle_shop/macros/pivot_sum.sql +7 -0
- specdbt-0.1.0/examples/jaffle_shop/models/customers.sql +69 -0
- specdbt-0.1.0/examples/jaffle_shop/models/order_history.sql +7 -0
- specdbt-0.1.0/examples/jaffle_shop/models/order_surrogate_keys.sql +4 -0
- specdbt-0.1.0/examples/jaffle_shop/models/orders.sql +56 -0
- specdbt-0.1.0/examples/jaffle_shop/models/schema.yml +45 -0
- specdbt-0.1.0/examples/jaffle_shop/models/staging/schema.yml +31 -0
- specdbt-0.1.0/examples/jaffle_shop/models/staging/stg_customers.sql +22 -0
- specdbt-0.1.0/examples/jaffle_shop/models/staging/stg_orders.sql +23 -0
- specdbt-0.1.0/examples/jaffle_shop/models/staging/stg_payments.sql +25 -0
- specdbt-0.1.0/examples/jaffle_shop/package-lock.yml +5 -0
- specdbt-0.1.0/examples/jaffle_shop/packages.yml +3 -0
- specdbt-0.1.0/examples/jaffle_shop/profiles/profiles.yml +7 -0
- specdbt-0.1.0/examples/jaffle_shop/seeds/raw_customers.csv +4 -0
- specdbt-0.1.0/examples/jaffle_shop/seeds/raw_orders.csv +4 -0
- specdbt-0.1.0/examples/jaffle_shop/seeds/raw_payments.csv +4 -0
- specdbt-0.1.0/pyproject.toml +79 -0
- specdbt-0.1.0/src/specdbt/__init__.py +0 -0
- specdbt-0.1.0/src/specdbt/adapters/__init__.py +0 -0
- specdbt-0.1.0/src/specdbt/adapters/base.py +39 -0
- specdbt-0.1.0/src/specdbt/adapters/dbt_adapter.py +111 -0
- specdbt-0.1.0/src/specdbt/adapters/fake_adapter.py +38 -0
- specdbt-0.1.0/src/specdbt/adapters/prod_guard.py +22 -0
- specdbt-0.1.0/src/specdbt/ai/__init__.py +0 -0
- specdbt-0.1.0/src/specdbt/ai/stubs.py +29 -0
- specdbt-0.1.0/src/specdbt/assertions.py +145 -0
- specdbt-0.1.0/src/specdbt/cli.py +168 -0
- specdbt-0.1.0/src/specdbt/dbt_integration/__init__.py +0 -0
- specdbt-0.1.0/src/specdbt/dbt_integration/fixture_sql.py +56 -0
- specdbt-0.1.0/src/specdbt/dbt_integration/macro_file.py +70 -0
- specdbt-0.1.0/src/specdbt/dbt_integration/ref_substitution.py +39 -0
- specdbt-0.1.0/src/specdbt/dbt_integration/relation_expr.py +20 -0
- specdbt-0.1.0/src/specdbt/dbt_integration/target_catalog.py +33 -0
- specdbt-0.1.0/src/specdbt/fixtures.py +38 -0
- specdbt-0.1.0/src/specdbt/native_unit_tests/__init__.py +0 -0
- specdbt-0.1.0/src/specdbt/native_unit_tests/compiler.py +75 -0
- specdbt-0.1.0/src/specdbt/native_unit_tests/model_compiler.py +123 -0
- specdbt-0.1.0/src/specdbt/native_unit_tests/model_unit_test_compiler.py +178 -0
- specdbt-0.1.0/src/specdbt/native_unit_tests/yaml_file.py +58 -0
- specdbt-0.1.0/src/specdbt/parser.py +86 -0
- specdbt-0.1.0/src/specdbt/py.typed +0 -0
- specdbt-0.1.0/src/specdbt/reporter.py +50 -0
- specdbt-0.1.0/src/specdbt/runner.py +118 -0
- specdbt-0.1.0/src/specdbt/sql_literals.py +46 -0
- specdbt-0.1.0/src/specdbt/typing_utils.py +47 -0
- specdbt-0.1.0/tests/conftest.py +135 -0
- specdbt-0.1.0/tests/dbt_integration/__init__.py +0 -0
- specdbt-0.1.0/tests/dbt_integration/test_cross_tier_catalog_consistency.py +44 -0
- specdbt-0.1.0/tests/dbt_integration/test_fixture_sql.py +78 -0
- specdbt-0.1.0/tests/dbt_integration/test_macro_file.py +55 -0
- specdbt-0.1.0/tests/dbt_integration/test_ref_substitution.py +57 -0
- specdbt-0.1.0/tests/dbt_integration/test_relation_expr.py +23 -0
- specdbt-0.1.0/tests/dbt_integration/test_target_catalog.py +61 -0
- specdbt-0.1.0/tests/native_unit_tests/__init__.py +0 -0
- specdbt-0.1.0/tests/native_unit_tests/test_compiler.py +76 -0
- specdbt-0.1.0/tests/native_unit_tests/test_model_compiler.py +221 -0
- specdbt-0.1.0/tests/native_unit_tests/test_model_unit_test_compiler.py +216 -0
- specdbt-0.1.0/tests/native_unit_tests/test_yaml_file.py +85 -0
- specdbt-0.1.0/tests/test_adapters.py +55 -0
- specdbt-0.1.0/tests/test_ai_stubs.py +23 -0
- specdbt-0.1.0/tests/test_assertions.py +158 -0
- specdbt-0.1.0/tests/test_cli.py +201 -0
- specdbt-0.1.0/tests/test_dbt_adapter.py +115 -0
- specdbt-0.1.0/tests/test_dbt_adapter_databricks.py +72 -0
- specdbt-0.1.0/tests/test_dbt_adapter_postgres.py +73 -0
- specdbt-0.1.0/tests/test_examples_jaffle_shop.py +50 -0
- specdbt-0.1.0/tests/test_fixtures.py +58 -0
- specdbt-0.1.0/tests/test_parser.py +101 -0
- specdbt-0.1.0/tests/test_reporter.py +54 -0
- specdbt-0.1.0/tests/test_runner.py +264 -0
- specdbt-0.1.0/tests/test_sql_literals.py +55 -0
- specdbt-0.1.0/tests/test_typing_utils.py +55 -0
- specdbt-0.1.0/uv.lock +2168 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: specdbt
|
|
3
|
+
description: Load specdbt's OKF architecture knowledge bundle (docs/knowledge/) as context before answering questions about this repo's design, then offer to refresh any doc that looks stale against current source.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# specdbt Knowledge Bundle
|
|
7
|
+
|
|
8
|
+
This repo's architecture reference lives in `docs/knowledge/`, an [Open
|
|
9
|
+
Knowledge Format](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md)
|
|
10
|
+
v0.2 bundle. Use it instead of a fresh grep/read of `src/` when answering
|
|
11
|
+
questions about specdbt's design.
|
|
12
|
+
|
|
13
|
+
## Steps
|
|
14
|
+
|
|
15
|
+
1. Read `docs/knowledge/index.md`.
|
|
16
|
+
2. Read every concept doc it links to (`pipeline.md`,
|
|
17
|
+
`two-tier-design.md`, `adapters.md`, `dbt-integration.md`,
|
|
18
|
+
`native-unit-tests.md`, `cli.md`, `ai.md`, `gherkin-style-guide.md`,
|
|
19
|
+
`databricks-validation-checklist.md`).
|
|
20
|
+
3. Answer the user's question grounded in that bundle. If something the
|
|
21
|
+
user asks about isn't covered, say so rather than guessing, and fall
|
|
22
|
+
back to reading the relevant source under `src/specdbt/`.
|
|
23
|
+
4. Before finishing, check whether anything you read in `src/` while
|
|
24
|
+
answering contradicts a concept doc. If so, tell the user which doc
|
|
25
|
+
looks stale and offer to update it — don't rewrite it automatically.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @SanaVarsi
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something isn't working as expected
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: description
|
|
7
|
+
attributes:
|
|
8
|
+
label: What happened?
|
|
9
|
+
description: What you expected instead, and what actually happened.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: repro
|
|
14
|
+
attributes:
|
|
15
|
+
label: Minimal reproduction
|
|
16
|
+
description: A minimal `.feature` file / model / command that reproduces it.
|
|
17
|
+
render: shell
|
|
18
|
+
- type: input
|
|
19
|
+
id: version
|
|
20
|
+
attributes:
|
|
21
|
+
label: specdbt version
|
|
22
|
+
description: Output of `specdbt --version` (or `pip show specdbt`).
|
|
23
|
+
validations:
|
|
24
|
+
required: true
|
|
25
|
+
- type: input
|
|
26
|
+
id: adapter
|
|
27
|
+
attributes:
|
|
28
|
+
label: dbt adapter
|
|
29
|
+
description: e.g. duckdb, postgres, databricks, snowflake
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest an idea or enhancement
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: What problem does this solve?
|
|
9
|
+
validations:
|
|
10
|
+
required: true
|
|
11
|
+
- type: textarea
|
|
12
|
+
id: proposal
|
|
13
|
+
attributes:
|
|
14
|
+
label: Proposed solution
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
# Keeps the SHA-pinned actions in ci.yml/release.yml current -- Dependabot
|
|
4
|
+
# bumps the pin and refreshes the version comment together.
|
|
5
|
+
- package-ecosystem: "github-actions"
|
|
6
|
+
directory: "/"
|
|
7
|
+
schedule:
|
|
8
|
+
interval: "weekly"
|
|
9
|
+
# Actions run inside CI with ambient trust (secrets, environment
|
|
10
|
+
# access) -- major bumps get a deliberate look, not auto-approval.
|
|
11
|
+
# Security-advisory PRs bypass this and land regardless.
|
|
12
|
+
ignore:
|
|
13
|
+
- dependency-name: "*"
|
|
14
|
+
update-types: ["version-update:semver-major"]
|
|
15
|
+
|
|
16
|
+
# uv.lock + pyproject.toml.
|
|
17
|
+
- package-ecosystem: "uv"
|
|
18
|
+
directory: "/"
|
|
19
|
+
schedule:
|
|
20
|
+
interval: "weekly"
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_call: # lets release.yml re-run this same suite before publishing
|
|
8
|
+
|
|
9
|
+
# Least-privilege default; jobs escalate only what they individually need.
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
gitleaks:
|
|
15
|
+
# Separate from `test`: the pre-commit gitleaks hook below only
|
|
16
|
+
# scans the staged diff (`--pre-commit --staged`), which is empty
|
|
17
|
+
# on `pre-commit run --all-files` against a clean CI checkout --
|
|
18
|
+
# it always passes trivially there regardless of what the repo
|
|
19
|
+
# actually contains. This job does the real full-history scan the
|
|
20
|
+
# pre-commit hook can't, via gitleaks-action, which needs full
|
|
21
|
+
# git history (fetch-depth: 0) to see past commits, not just HEAD.
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
# gitleaks-action only understands push/pull_request -- release.yml
|
|
24
|
+
# calls this workflow with github.event_name still "release" (it
|
|
25
|
+
# propagates from the caller), which the action rejects outright.
|
|
26
|
+
# Code reaching a release already went through this scan on its
|
|
27
|
+
# original push/PR, so skipping here is a no-op, not a gap.
|
|
28
|
+
if: github.event_name == 'push' || github.event_name == 'pull_request'
|
|
29
|
+
permissions:
|
|
30
|
+
contents: read
|
|
31
|
+
# gitleaks-action calls the GitHub API to list the PR's commits and
|
|
32
|
+
# scope the scan to them on a pull_request event -- without this,
|
|
33
|
+
# the default token has no pull-requests scope and the call 403s.
|
|
34
|
+
pull-requests: read
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
37
|
+
with:
|
|
38
|
+
fetch-depth: 0
|
|
39
|
+
- uses: gitleaks/gitleaks-action@e0c47f4f8be36e29cdc102c57e68cb5cbf0e8d1e # v3.0.0
|
|
40
|
+
env:
|
|
41
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
42
|
+
|
|
43
|
+
test:
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
services:
|
|
46
|
+
postgres:
|
|
47
|
+
image: postgres:16
|
|
48
|
+
env:
|
|
49
|
+
POSTGRES_USER: specdbt
|
|
50
|
+
POSTGRES_DB: specdbt_test
|
|
51
|
+
POSTGRES_PASSWORD: ${{ secrets.SPECDBT_PG_SECRET }}
|
|
52
|
+
ports:
|
|
53
|
+
- 5432:5432
|
|
54
|
+
options: >-
|
|
55
|
+
--health-cmd pg_isready
|
|
56
|
+
--health-interval 10s
|
|
57
|
+
--health-timeout 5s
|
|
58
|
+
--health-retries 5
|
|
59
|
+
steps:
|
|
60
|
+
# Full history + tags -- hatch-vcs derives the package version from
|
|
61
|
+
# the nearest git tag, which a shallow checkout can't see.
|
|
62
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
63
|
+
with:
|
|
64
|
+
fetch-depth: 0
|
|
65
|
+
|
|
66
|
+
- name: Install uv
|
|
67
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
68
|
+
with:
|
|
69
|
+
enable-cache: true
|
|
70
|
+
# Pinned to match the uv-pre-commit hook's uv version below --
|
|
71
|
+
# an unpinned uv here could rewrite uv.lock to a newer lockfile
|
|
72
|
+
# revision than the hook's `uv lock --check` expects, failing
|
|
73
|
+
# it for a reason that has nothing to do with an actual drift.
|
|
74
|
+
version: "0.11.25"
|
|
75
|
+
|
|
76
|
+
- name: Install dependencies
|
|
77
|
+
run: uv sync --all-extras
|
|
78
|
+
|
|
79
|
+
- name: Cache pre-commit environments
|
|
80
|
+
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
81
|
+
with:
|
|
82
|
+
path: ~/.cache/pre-commit
|
|
83
|
+
key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
|
|
84
|
+
|
|
85
|
+
- name: Pre-commit hooks (lint, formatting, type-check, hygiene)
|
|
86
|
+
run: uv run pre-commit run --all-files --show-diff-on-failure
|
|
87
|
+
|
|
88
|
+
- name: Install dbt_utils for the example project
|
|
89
|
+
working-directory: examples/jaffle_shop
|
|
90
|
+
run: uv run --project ../.. dbt deps --profiles-dir profiles
|
|
91
|
+
|
|
92
|
+
- name: Test
|
|
93
|
+
env:
|
|
94
|
+
SPECDBT_TEST_POSTGRES: "1"
|
|
95
|
+
SPECDBT_PG_HOST: localhost
|
|
96
|
+
SPECDBT_PG_PORT: "5432"
|
|
97
|
+
SPECDBT_PG_USER: specdbt
|
|
98
|
+
SPECDBT_PG_DBNAME: specdbt_test
|
|
99
|
+
SPECDBT_PG_SECRET: ${{ secrets.SPECDBT_PG_SECRET }}
|
|
100
|
+
run: uv run pytest -v
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Trusted publishing to PyPI (OIDC, no API token secret). One-time setup:
|
|
4
|
+
# 1. On pypi.org, add a *pending* trusted publisher for project "specdbt"
|
|
5
|
+
# naming this owner/repo, workflow filename "release.yml", and
|
|
6
|
+
# environment "pypi" -- exactly, all three are load-bearing.
|
|
7
|
+
# 2. Create a GitHub Environment named "pypi" on this repo.
|
|
8
|
+
#
|
|
9
|
+
# Two ways to release, both land here:
|
|
10
|
+
# - Actions tab -> Run workflow -> pick a bump (or type an exact
|
|
11
|
+
# version). The `tag` job below computes the next vX.Y.Z, tags it,
|
|
12
|
+
# and opens the GitHub Release itself, then this same run continues
|
|
13
|
+
# into test/build/publish -- a release created by a workflow's own
|
|
14
|
+
# GITHUB_TOKEN can't trigger other workflows (GitHub's anti-loop
|
|
15
|
+
# protection), so continuing in-run is what makes this work without
|
|
16
|
+
# a personal access token.
|
|
17
|
+
# - Publishing a GitHub Release by hand (a human token, which *does*
|
|
18
|
+
# trigger events) fires this directly via `release: published`; the
|
|
19
|
+
# `tag` job just skips itself since the tag already exists.
|
|
20
|
+
#
|
|
21
|
+
# Either way, pyproject.toml's version is derived from the tag via
|
|
22
|
+
# hatch-vcs -- nothing to hand-edit or keep in sync.
|
|
23
|
+
on:
|
|
24
|
+
release:
|
|
25
|
+
types: [published]
|
|
26
|
+
workflow_dispatch:
|
|
27
|
+
inputs:
|
|
28
|
+
bump:
|
|
29
|
+
description: "Version bump (ignored if 'version' is set)"
|
|
30
|
+
type: choice
|
|
31
|
+
options: [patch, minor, major]
|
|
32
|
+
default: patch
|
|
33
|
+
version:
|
|
34
|
+
description: "Exact version to release instead, e.g. 1.2.0 (optional)"
|
|
35
|
+
type: string
|
|
36
|
+
default: ""
|
|
37
|
+
|
|
38
|
+
# Least-privilege default; jobs escalate only what they individually need.
|
|
39
|
+
permissions:
|
|
40
|
+
contents: read
|
|
41
|
+
|
|
42
|
+
jobs:
|
|
43
|
+
tag:
|
|
44
|
+
if: github.event_name == 'workflow_dispatch'
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
permissions:
|
|
47
|
+
contents: write # to create the tag + GitHub Release
|
|
48
|
+
outputs:
|
|
49
|
+
version: ${{ steps.next.outputs.version }}
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
52
|
+
with:
|
|
53
|
+
fetch-depth: 0 # need full tag history to find the latest release
|
|
54
|
+
|
|
55
|
+
- name: Compute next version
|
|
56
|
+
id: next
|
|
57
|
+
run: |
|
|
58
|
+
set -euo pipefail
|
|
59
|
+
if [ -n "${{ inputs.version }}" ]; then
|
|
60
|
+
next="${{ inputs.version }}"
|
|
61
|
+
else
|
|
62
|
+
latest="$(git tag --list 'v*' --sort=-v:refname | head -1)"
|
|
63
|
+
latest="${latest#v}"
|
|
64
|
+
latest="${latest:-0.0.0}"
|
|
65
|
+
IFS='.' read -r major minor patch <<< "$latest"
|
|
66
|
+
case "${{ inputs.bump }}" in
|
|
67
|
+
major) next="$((major + 1)).0.0" ;;
|
|
68
|
+
minor) next="$major.$((minor + 1)).0" ;;
|
|
69
|
+
patch) next="$major.$minor.$((patch + 1))" ;;
|
|
70
|
+
esac
|
|
71
|
+
fi
|
|
72
|
+
if git rev-parse "v$next" >/dev/null 2>&1; then
|
|
73
|
+
echo "tag v$next already exists" >&2
|
|
74
|
+
exit 1
|
|
75
|
+
fi
|
|
76
|
+
echo "version=$next" >> "$GITHUB_OUTPUT"
|
|
77
|
+
|
|
78
|
+
- name: Tag + publish release
|
|
79
|
+
env:
|
|
80
|
+
GH_TOKEN: ${{ github.token }}
|
|
81
|
+
NEXT: ${{ steps.next.outputs.version }}
|
|
82
|
+
run: |
|
|
83
|
+
set -euo pipefail
|
|
84
|
+
git config user.name "github-actions[bot]"
|
|
85
|
+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
86
|
+
git tag -a "v$NEXT" -m "specdbt v$NEXT"
|
|
87
|
+
git push origin "v$NEXT"
|
|
88
|
+
gh release create "v$NEXT" \
|
|
89
|
+
--title "v$NEXT" \
|
|
90
|
+
--generate-notes \
|
|
91
|
+
--repo "${{ github.repository }}"
|
|
92
|
+
|
|
93
|
+
# Reruns the same suite ci.yml runs on every push/PR -- a merged, green
|
|
94
|
+
# commit doesn't guarantee the exact tagged ref is still green, and this
|
|
95
|
+
# is the last gate before an irreversible PyPI upload.
|
|
96
|
+
test:
|
|
97
|
+
needs: tag
|
|
98
|
+
if: always() && (needs.tag.result == 'success' || needs.tag.result == 'skipped')
|
|
99
|
+
uses: ./.github/workflows/ci.yml
|
|
100
|
+
secrets: inherit
|
|
101
|
+
# ci.yml's gitleaks job needs pull-requests: read for its own scan --
|
|
102
|
+
# a called workflow can't exceed what the caller grants it here.
|
|
103
|
+
permissions:
|
|
104
|
+
contents: read
|
|
105
|
+
pull-requests: read
|
|
106
|
+
|
|
107
|
+
build:
|
|
108
|
+
needs: [tag, test]
|
|
109
|
+
runs-on: ubuntu-latest
|
|
110
|
+
permissions:
|
|
111
|
+
contents: read
|
|
112
|
+
steps:
|
|
113
|
+
# Full history + tags -- hatch-vcs derives the package version from
|
|
114
|
+
# the tag. Pin explicitly to it when the `tag` job ran (workflow_dispatch)
|
|
115
|
+
# so a commit landing on main between tagging and this checkout can't
|
|
116
|
+
# slip in ahead of the release it's building; a release-event run
|
|
117
|
+
# already has the tag as github.ref.
|
|
118
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
119
|
+
with:
|
|
120
|
+
fetch-depth: 0
|
|
121
|
+
ref: ${{ needs.tag.outputs.version && format('v{0}', needs.tag.outputs.version) || github.ref }}
|
|
122
|
+
|
|
123
|
+
- name: Install uv
|
|
124
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
125
|
+
with:
|
|
126
|
+
enable-cache: true
|
|
127
|
+
version: "0.11.25"
|
|
128
|
+
|
|
129
|
+
- name: Build sdist + wheel
|
|
130
|
+
run: uv build
|
|
131
|
+
|
|
132
|
+
- name: Built version matches release tag
|
|
133
|
+
# Catches hatch-vcs silently falling back to a dev version (e.g.
|
|
134
|
+
# the tag wasn't actually visible to the checkout above) before it
|
|
135
|
+
# reaches PyPI -- the one thing the old hand-written version field
|
|
136
|
+
# let us check directly. sdist filename is specdbt-X.Y.Z.tar.gz.
|
|
137
|
+
env:
|
|
138
|
+
TAGGED_VERSION: ${{ needs.tag.outputs.version }}
|
|
139
|
+
run: |
|
|
140
|
+
tag="${TAGGED_VERSION:-${GITHUB_REF_NAME#v}}"
|
|
141
|
+
built="$(basename dist/specdbt-*.tar.gz .tar.gz | sed 's/^specdbt-//')"
|
|
142
|
+
if [ "$tag" != "$built" ]; then
|
|
143
|
+
echo "release tag v$tag does not match built version $built"
|
|
144
|
+
exit 1
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
148
|
+
with:
|
|
149
|
+
name: dist
|
|
150
|
+
path: dist/
|
|
151
|
+
|
|
152
|
+
publish:
|
|
153
|
+
needs: build
|
|
154
|
+
runs-on: ubuntu-latest
|
|
155
|
+
environment: pypi
|
|
156
|
+
permissions:
|
|
157
|
+
id-token: write # OIDC for trusted publishing -- no token secret needed
|
|
158
|
+
contents: read
|
|
159
|
+
attestations: write # PEP 740 build provenance attestation
|
|
160
|
+
steps:
|
|
161
|
+
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
162
|
+
with:
|
|
163
|
+
name: dist
|
|
164
|
+
path: dist/
|
|
165
|
+
|
|
166
|
+
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
specdbt-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
.venv/
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.pytest_cache/
|
|
8
|
+
.ruff_cache/
|
|
9
|
+
.DS_Store
|
|
10
|
+
|
|
11
|
+
# Local tool config/state, not project source
|
|
12
|
+
.serena/
|
|
13
|
+
.superpowers/
|
|
14
|
+
.claude/settings.local.json
|
|
15
|
+
local_work/
|
|
16
|
+
|
|
17
|
+
# dbt example projects (examples/**): real dbt run artifacts, never source
|
|
18
|
+
examples/**/dbt_packages/
|
|
19
|
+
examples/**/logs/
|
|
20
|
+
examples/**/target/
|
|
21
|
+
examples/**/*.duckdb
|
|
22
|
+
examples/**/.user.yml
|
|
23
|
+
# Test runs with a relative `path:` in a scratch profiles.yml resolve it
|
|
24
|
+
# against the process cwd, not --project-dir -- stray .duckdb files can end
|
|
25
|
+
# up at the repo root when tests run from here. Not committed either way,
|
|
26
|
+
# but keep them out of `git status` noise.
|
|
27
|
+
/*.duckdb
|
|
28
|
+
logs/
|
|
29
|
+
.worktrees/
|
|
30
|
+
.env
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
3
|
+
rev: v0.16.5
|
|
4
|
+
hooks:
|
|
5
|
+
- id: ruff-check
|
|
6
|
+
- id: ruff-format
|
|
7
|
+
|
|
8
|
+
- repo: https://github.com/astral-sh/ty-pre-commit
|
|
9
|
+
rev: v0.0.75
|
|
10
|
+
hooks:
|
|
11
|
+
- id: ty
|
|
12
|
+
|
|
13
|
+
- repo: https://github.com/astral-sh/uv-pre-commit
|
|
14
|
+
rev: "0.11.25"
|
|
15
|
+
hooks:
|
|
16
|
+
- id: uv-lock
|
|
17
|
+
|
|
18
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
19
|
+
rev: v6.0.0
|
|
20
|
+
hooks:
|
|
21
|
+
- id: trailing-whitespace
|
|
22
|
+
- id: end-of-file-fixer
|
|
23
|
+
- id: check-yaml
|
|
24
|
+
- id: check-toml
|
|
25
|
+
- id: check-ast
|
|
26
|
+
- id: check-added-large-files
|
|
27
|
+
- id: check-merge-conflict
|
|
28
|
+
- id: debug-statements
|
|
29
|
+
- id: mixed-line-ending
|
|
30
|
+
|
|
31
|
+
- repo: https://github.com/crate-ci/typos
|
|
32
|
+
rev: v1.50.0
|
|
33
|
+
hooks:
|
|
34
|
+
- id: typos
|
|
35
|
+
# docs/ holds prose specs/plans with technical shorthand typos
|
|
36
|
+
# flags; examples/ has generated dbt_packages/target dirs that
|
|
37
|
+
# are gitignored but present locally during a run.
|
|
38
|
+
exclude: ^(docs/|examples/.*/(dbt_packages|target|logs)/)
|
|
39
|
+
|
|
40
|
+
- repo: https://github.com/abravalheri/validate-pyproject
|
|
41
|
+
rev: v0.25
|
|
42
|
+
hooks:
|
|
43
|
+
- id: validate-pyproject
|
|
44
|
+
|
|
45
|
+
- repo: https://github.com/gitleaks/gitleaks
|
|
46
|
+
rev: v8.30.1
|
|
47
|
+
hooks:
|
|
48
|
+
- id: gitleaks
|
specdbt-0.1.0/AGENTS.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# specdbt — Agent Notes
|
|
2
|
+
|
|
3
|
+
For any question about this repo's design, conventions, or testing
|
|
4
|
+
approach, consult the OKF knowledge bundle at
|
|
5
|
+
[`docs/knowledge/`](docs/knowledge/index.md) first — it is the source of
|
|
6
|
+
truth for this repo's architecture and conventions. Only fall back to
|
|
7
|
+
reading `src/specdbt/` from scratch when the bundle doesn't cover what
|
|
8
|
+
you need.
|
|
9
|
+
|
|
10
|
+
## Conventions
|
|
11
|
+
|
|
12
|
+
- Tests mirror `src/specdbt/` under `tests/`, with the package level
|
|
13
|
+
flattened (e.g. `src/specdbt/runner.py` -> `tests/test_runner.py`,
|
|
14
|
+
`src/specdbt/dbt_integration/fixture_sql.py` ->
|
|
15
|
+
`tests/dbt_integration/test_fixture_sql.py`) — not always 1:1; some
|
|
16
|
+
source files share a test file or split across several.
|
|
17
|
+
- Gherkin scenario style: see
|
|
18
|
+
[`docs/knowledge/gherkin-style-guide.md`](docs/knowledge/gherkin-style-guide.md).
|
|
19
|
+
- Databricks-specific test considerations: see
|
|
20
|
+
[`docs/knowledge/databricks-validation-checklist.md`](docs/knowledge/databricks-validation-checklist.md).
|
specdbt-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Contributing to specdbt
|
|
2
|
+
|
|
3
|
+
Issues, ideas, and PRs are welcome.
|
|
4
|
+
|
|
5
|
+
## Before you start
|
|
6
|
+
|
|
7
|
+
[`docs/knowledge/index.md`](docs/knowledge/index.md) is the source of
|
|
8
|
+
truth for this repo's architecture and conventions — read it (or have
|
|
9
|
+
your agent load it via the `/specdbt` skill) before making non-trivial
|
|
10
|
+
changes. `AGENTS.md` covers test-file layout and links the
|
|
11
|
+
[Gherkin style guide](docs/knowledge/gherkin-style-guide.md) for writing
|
|
12
|
+
`.feature` scenarios.
|
|
13
|
+
|
|
14
|
+
The `ExecutionAdapter` interface (`src/specdbt/adapters/base.py`) is the
|
|
15
|
+
extension point for a new backend — one new class, not a rewrite.
|
|
16
|
+
|
|
17
|
+
## Local setup
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
just setup # uv sync + pre-commit install (see `just doctor` if uv/Docker is missing)
|
|
21
|
+
just test # full test suite
|
|
22
|
+
```
|
|
23
|
+
Without `just`: `uv sync && uv run pre-commit install && uv run pytest`.
|
|
24
|
+
|
|
25
|
+
Pre-commit runs ruff, ty, uv-lock, typos, gitleaks, and hygiene checks on
|
|
26
|
+
every commit; CI runs the identical set (`pre-commit run --all-files`).
|
|
27
|
+
Exception: `gitleaks` pre-commit only scans the staged diff, so CI also
|
|
28
|
+
runs a separate `gitleaks-action` job over full history.
|
|
29
|
+
|
|
30
|
+
`uv run pytest` alone only exercises DuckDB. Two more test files cover
|
|
31
|
+
other adapters, both skipped unless you opt in:
|
|
32
|
+
|
|
33
|
+
**Postgres** (CI-verified, runnable locally with Docker):
|
|
34
|
+
```bash
|
|
35
|
+
just postgres-up # starts Postgres in Docker, writes .env if missing
|
|
36
|
+
just test-postgres # exports the right env vars, runs the test
|
|
37
|
+
```
|
|
38
|
+
Manual path: create a gitignored `.env` with `POSTGRES_USER`,
|
|
39
|
+
`POSTGRES_PASSWORD`, `POSTGRES_DB`; `docker compose up -d postgres`; then
|
|
40
|
+
export those same values as `SPECDBT_PG_USER`/`SPECDBT_PG_SECRET`/
|
|
41
|
+
`SPECDBT_PG_DBNAME` plus `SPECDBT_PG_HOST=localhost SPECDBT_PG_PORT=5432
|
|
42
|
+
SPECDBT_TEST_POSTGRES=1` and run `uv run pytest
|
|
43
|
+
tests/test_dbt_adapter_postgres.py -v` (see `tests/conftest.py` for why
|
|
44
|
+
the names differ).
|
|
45
|
+
|
|
46
|
+
**Databricks** (manual, needs your own workspace, no CI): see
|
|
47
|
+
`docs/knowledge/databricks-validation-checklist.md`.
|
|
48
|
+
|
|
49
|
+
## Pull requests
|
|
50
|
+
|
|
51
|
+
- Keep tests mirroring `src/specdbt/` under `tests/` (see `AGENTS.md`).
|
|
52
|
+
- `uv run pre-commit run --all-files` and `uv run pytest` clean before
|
|
53
|
+
pushing.
|
|
54
|
+
- Describe the behavior change, not just the diff — a scenario or test
|
|
55
|
+
demonstrating it is the strongest review evidence.
|
|
56
|
+
|
|
57
|
+
## Release process (maintainers)
|
|
58
|
+
|
|
59
|
+
Versioning is manual. Bump `version` in `pyproject.toml`, then tag and
|
|
60
|
+
publish a GitHub Release from that commit. The tag must be `vX.Y.Z`,
|
|
61
|
+
matching the `pyproject.toml` value exactly.
|
|
62
|
+
|
|
63
|
+
The release workflow then runs on publish: full CI suite against that
|
|
64
|
+
tag, build, and a PyPI publish step using trusted publishing, so there is
|
|
65
|
+
no stored API token.
|
|
66
|
+
|
|
67
|
+
One-time repo setup this depends on, all separate from anything in this
|
|
68
|
+
repo's code:
|
|
69
|
+
- a PyPI trusted publisher entry pointing at this repo
|
|
70
|
+
- a GitHub environment named `pypi`
|
|
71
|
+
- a repo secret the CI Postgres job reads (see `ci.yml`)
|
specdbt-0.1.0/Justfile
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# One-command local dev setup. Install `just` first:
|
|
2
|
+
# macOS: brew install just
|
|
3
|
+
# other: https://github.com/casey/just#installation
|
|
4
|
+
# Then: `just setup` once, `just` (no args) to list targets.
|
|
5
|
+
|
|
6
|
+
default:
|
|
7
|
+
@just --list
|
|
8
|
+
|
|
9
|
+
# Bootstrap everything needed to develop: python deps, git hooks.
|
|
10
|
+
setup:
|
|
11
|
+
#!/usr/bin/env bash
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
command -v uv >/dev/null 2>&1 || {
|
|
14
|
+
echo "uv not found. Install: curl -LsSf https://astral.sh/uv/install.sh | sh"
|
|
15
|
+
exit 1
|
|
16
|
+
}
|
|
17
|
+
uv sync
|
|
18
|
+
uv run pre-commit install
|
|
19
|
+
echo "Setup done. Try: just test"
|
|
20
|
+
|
|
21
|
+
# Check for tools this repo uses and print an install hint for anything missing.
|
|
22
|
+
doctor:
|
|
23
|
+
#!/usr/bin/env bash
|
|
24
|
+
set -euo pipefail
|
|
25
|
+
ok=1
|
|
26
|
+
check() {
|
|
27
|
+
if command -v "$1" >/dev/null 2>&1; then
|
|
28
|
+
echo " ok $1"
|
|
29
|
+
else
|
|
30
|
+
echo " MISSING $1 -- $2"
|
|
31
|
+
ok=0
|
|
32
|
+
fi
|
|
33
|
+
}
|
|
34
|
+
check uv "curl -LsSf https://astral.sh/uv/install.sh | sh"
|
|
35
|
+
check docker "https://docs.docker.com/get-docker/ (only needed for 'just postgres-up')"
|
|
36
|
+
[ "$ok" = 1 ] && echo "All required tools present." || { echo "Fix the above, then re-run 'just doctor'."; exit 1; }
|
|
37
|
+
|
|
38
|
+
# Full test suite -- DuckDB only, no external services required.
|
|
39
|
+
test:
|
|
40
|
+
uv run pytest
|
|
41
|
+
|
|
42
|
+
# Scaffold DIR with an example .feature file + canned result (default: features/).
|
|
43
|
+
init dir="features":
|
|
44
|
+
uv run specdbt init {{dir}}
|
|
45
|
+
|
|
46
|
+
# Run .feature files under TARGET (--engine fake by default); ARGS pass through to `specdbt run`.
|
|
47
|
+
run target="features" *args:
|
|
48
|
+
uv run specdbt run {{target}} {{args}}
|
|
49
|
+
|
|
50
|
+
# Run the bundled jaffle_shop example against real dbt+DuckDB (both tiers).
|
|
51
|
+
run-example:
|
|
52
|
+
#!/usr/bin/env bash
|
|
53
|
+
set -euo pipefail
|
|
54
|
+
(cd examples/jaffle_shop && uv run dbt deps --profiles-dir profiles)
|
|
55
|
+
uv run specdbt run examples/jaffle_shop/features \
|
|
56
|
+
--engine dbt \
|
|
57
|
+
--project-dir examples/jaffle_shop \
|
|
58
|
+
--profiles-dir examples/jaffle_shop/profiles
|
|
59
|
+
|
|
60
|
+
# Start local Postgres in Docker for the adapter test (generates .env on first run).
|
|
61
|
+
postgres-up:
|
|
62
|
+
#!/usr/bin/env bash
|
|
63
|
+
set -euo pipefail
|
|
64
|
+
command -v docker >/dev/null 2>&1 || {
|
|
65
|
+
echo "docker not found. Install: https://docs.docker.com/get-docker/"
|
|
66
|
+
exit 1
|
|
67
|
+
}
|
|
68
|
+
if [ ! -f .env ]; then
|
|
69
|
+
generated="$(openssl rand -hex 12)"
|
|
70
|
+
{
|
|
71
|
+
echo "POSTGRES_USER=specdbt"
|
|
72
|
+
printf 'POSTGRES_%s=%s\n' "PASSWORD" "$generated"
|
|
73
|
+
echo "POSTGRES_DB=specdbt_test"
|
|
74
|
+
} > .env
|
|
75
|
+
echo "Wrote .env with a freshly generated local-only credential (gitignored)."
|
|
76
|
+
fi
|
|
77
|
+
docker compose up -d postgres
|
|
78
|
+
echo "Postgres is up on localhost:5432. Next: just test-postgres"
|
|
79
|
+
|
|
80
|
+
# Run the Postgres adapter test against the container from 'postgres-up'.
|
|
81
|
+
test-postgres:
|
|
82
|
+
#!/usr/bin/env bash
|
|
83
|
+
set -euo pipefail
|
|
84
|
+
[ -f .env ] || { echo "No .env found -- run 'just postgres-up' first."; exit 1; }
|
|
85
|
+
set -a; source .env; set +a
|
|
86
|
+
export SPECDBT_PG_USER="$POSTGRES_USER"
|
|
87
|
+
export SPECDBT_PG_SECRET="$POSTGRES_PASSWORD"
|
|
88
|
+
export SPECDBT_PG_DBNAME="$POSTGRES_DB"
|
|
89
|
+
export SPECDBT_PG_HOST=localhost SPECDBT_PG_PORT=5432 SPECDBT_TEST_POSTGRES=1
|
|
90
|
+
uv run pytest tests/test_dbt_adapter_postgres.py -v
|
specdbt-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SanaVarsi
|
|
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.
|