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.
Files changed (116) hide show
  1. specdbt-0.1.0/.claude/skills/specdbt/SKILL.md +25 -0
  2. specdbt-0.1.0/.github/CODEOWNERS +1 -0
  3. specdbt-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +29 -0
  4. specdbt-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  5. specdbt-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +14 -0
  6. specdbt-0.1.0/.github/dependabot.yml +20 -0
  7. specdbt-0.1.0/.github/pull_request_template.md +7 -0
  8. specdbt-0.1.0/.github/workflows/ci.yml +100 -0
  9. specdbt-0.1.0/.github/workflows/release.yml +166 -0
  10. specdbt-0.1.0/.gitignore +30 -0
  11. specdbt-0.1.0/.pre-commit-config.yaml +48 -0
  12. specdbt-0.1.0/AGENTS.md +20 -0
  13. specdbt-0.1.0/CLAUDE.md +5 -0
  14. specdbt-0.1.0/CONTRIBUTING.md +71 -0
  15. specdbt-0.1.0/Justfile +90 -0
  16. specdbt-0.1.0/LICENSE +21 -0
  17. specdbt-0.1.0/PKG-INFO +95 -0
  18. specdbt-0.1.0/README.md +65 -0
  19. specdbt-0.1.0/SECURITY.md +13 -0
  20. specdbt-0.1.0/docker-compose.yml +6 -0
  21. specdbt-0.1.0/docs/knowledge/adapters.md +37 -0
  22. specdbt-0.1.0/docs/knowledge/ai.md +19 -0
  23. specdbt-0.1.0/docs/knowledge/cli.md +19 -0
  24. specdbt-0.1.0/docs/knowledge/databricks-validation-checklist.md +42 -0
  25. specdbt-0.1.0/docs/knowledge/dbt-integration.md +42 -0
  26. specdbt-0.1.0/docs/knowledge/gherkin-style-guide.md +82 -0
  27. specdbt-0.1.0/docs/knowledge/index.md +27 -0
  28. specdbt-0.1.0/docs/knowledge/native-unit-tests.md +38 -0
  29. specdbt-0.1.0/docs/knowledge/pipeline.md +55 -0
  30. specdbt-0.1.0/docs/knowledge/two-tier-design.md +64 -0
  31. specdbt-0.1.0/examples/jaffle_shop/dbt_project.yml +23 -0
  32. specdbt-0.1.0/examples/jaffle_shop/features/macros/bucket_order_value/bucket_order_value.feature +36 -0
  33. specdbt-0.1.0/examples/jaffle_shop/features/macros/generate_surrogate_key/generate_surrogate_key.feature +12 -0
  34. specdbt-0.1.0/examples/jaffle_shop/features/macros/generate_surrogate_key/surrogate_key_quality.feature +24 -0
  35. specdbt-0.1.0/examples/jaffle_shop/features/macros/order_value_summary/order_value_summary.feature +15 -0
  36. specdbt-0.1.0/examples/jaffle_shop/features/macros/pivot_sum/pivot_sum.feature +16 -0
  37. specdbt-0.1.0/examples/jaffle_shop/features/macros/star/star.feature +12 -0
  38. specdbt-0.1.0/examples/jaffle_shop/features/models/customers/customers.feature +19 -0
  39. specdbt-0.1.0/examples/jaffle_shop/features/models/order_history/order_history.feature +41 -0
  40. specdbt-0.1.0/examples/jaffle_shop/features/models/order_surrogate_keys/order_surrogate_keys.feature +18 -0
  41. specdbt-0.1.0/examples/jaffle_shop/features/models/stg_customers/stg_customers.feature +11 -0
  42. specdbt-0.1.0/examples/jaffle_shop/macros/bucket_order_value.sql +7 -0
  43. specdbt-0.1.0/examples/jaffle_shop/macros/order_value_summary.sql +7 -0
  44. specdbt-0.1.0/examples/jaffle_shop/macros/pivot_sum.sql +7 -0
  45. specdbt-0.1.0/examples/jaffle_shop/models/customers.sql +69 -0
  46. specdbt-0.1.0/examples/jaffle_shop/models/order_history.sql +7 -0
  47. specdbt-0.1.0/examples/jaffle_shop/models/order_surrogate_keys.sql +4 -0
  48. specdbt-0.1.0/examples/jaffle_shop/models/orders.sql +56 -0
  49. specdbt-0.1.0/examples/jaffle_shop/models/schema.yml +45 -0
  50. specdbt-0.1.0/examples/jaffle_shop/models/staging/schema.yml +31 -0
  51. specdbt-0.1.0/examples/jaffle_shop/models/staging/stg_customers.sql +22 -0
  52. specdbt-0.1.0/examples/jaffle_shop/models/staging/stg_orders.sql +23 -0
  53. specdbt-0.1.0/examples/jaffle_shop/models/staging/stg_payments.sql +25 -0
  54. specdbt-0.1.0/examples/jaffle_shop/package-lock.yml +5 -0
  55. specdbt-0.1.0/examples/jaffle_shop/packages.yml +3 -0
  56. specdbt-0.1.0/examples/jaffle_shop/profiles/profiles.yml +7 -0
  57. specdbt-0.1.0/examples/jaffle_shop/seeds/raw_customers.csv +4 -0
  58. specdbt-0.1.0/examples/jaffle_shop/seeds/raw_orders.csv +4 -0
  59. specdbt-0.1.0/examples/jaffle_shop/seeds/raw_payments.csv +4 -0
  60. specdbt-0.1.0/pyproject.toml +79 -0
  61. specdbt-0.1.0/src/specdbt/__init__.py +0 -0
  62. specdbt-0.1.0/src/specdbt/adapters/__init__.py +0 -0
  63. specdbt-0.1.0/src/specdbt/adapters/base.py +39 -0
  64. specdbt-0.1.0/src/specdbt/adapters/dbt_adapter.py +111 -0
  65. specdbt-0.1.0/src/specdbt/adapters/fake_adapter.py +38 -0
  66. specdbt-0.1.0/src/specdbt/adapters/prod_guard.py +22 -0
  67. specdbt-0.1.0/src/specdbt/ai/__init__.py +0 -0
  68. specdbt-0.1.0/src/specdbt/ai/stubs.py +29 -0
  69. specdbt-0.1.0/src/specdbt/assertions.py +145 -0
  70. specdbt-0.1.0/src/specdbt/cli.py +168 -0
  71. specdbt-0.1.0/src/specdbt/dbt_integration/__init__.py +0 -0
  72. specdbt-0.1.0/src/specdbt/dbt_integration/fixture_sql.py +56 -0
  73. specdbt-0.1.0/src/specdbt/dbt_integration/macro_file.py +70 -0
  74. specdbt-0.1.0/src/specdbt/dbt_integration/ref_substitution.py +39 -0
  75. specdbt-0.1.0/src/specdbt/dbt_integration/relation_expr.py +20 -0
  76. specdbt-0.1.0/src/specdbt/dbt_integration/target_catalog.py +33 -0
  77. specdbt-0.1.0/src/specdbt/fixtures.py +38 -0
  78. specdbt-0.1.0/src/specdbt/native_unit_tests/__init__.py +0 -0
  79. specdbt-0.1.0/src/specdbt/native_unit_tests/compiler.py +75 -0
  80. specdbt-0.1.0/src/specdbt/native_unit_tests/model_compiler.py +123 -0
  81. specdbt-0.1.0/src/specdbt/native_unit_tests/model_unit_test_compiler.py +178 -0
  82. specdbt-0.1.0/src/specdbt/native_unit_tests/yaml_file.py +58 -0
  83. specdbt-0.1.0/src/specdbt/parser.py +86 -0
  84. specdbt-0.1.0/src/specdbt/py.typed +0 -0
  85. specdbt-0.1.0/src/specdbt/reporter.py +50 -0
  86. specdbt-0.1.0/src/specdbt/runner.py +118 -0
  87. specdbt-0.1.0/src/specdbt/sql_literals.py +46 -0
  88. specdbt-0.1.0/src/specdbt/typing_utils.py +47 -0
  89. specdbt-0.1.0/tests/conftest.py +135 -0
  90. specdbt-0.1.0/tests/dbt_integration/__init__.py +0 -0
  91. specdbt-0.1.0/tests/dbt_integration/test_cross_tier_catalog_consistency.py +44 -0
  92. specdbt-0.1.0/tests/dbt_integration/test_fixture_sql.py +78 -0
  93. specdbt-0.1.0/tests/dbt_integration/test_macro_file.py +55 -0
  94. specdbt-0.1.0/tests/dbt_integration/test_ref_substitution.py +57 -0
  95. specdbt-0.1.0/tests/dbt_integration/test_relation_expr.py +23 -0
  96. specdbt-0.1.0/tests/dbt_integration/test_target_catalog.py +61 -0
  97. specdbt-0.1.0/tests/native_unit_tests/__init__.py +0 -0
  98. specdbt-0.1.0/tests/native_unit_tests/test_compiler.py +76 -0
  99. specdbt-0.1.0/tests/native_unit_tests/test_model_compiler.py +221 -0
  100. specdbt-0.1.0/tests/native_unit_tests/test_model_unit_test_compiler.py +216 -0
  101. specdbt-0.1.0/tests/native_unit_tests/test_yaml_file.py +85 -0
  102. specdbt-0.1.0/tests/test_adapters.py +55 -0
  103. specdbt-0.1.0/tests/test_ai_stubs.py +23 -0
  104. specdbt-0.1.0/tests/test_assertions.py +158 -0
  105. specdbt-0.1.0/tests/test_cli.py +201 -0
  106. specdbt-0.1.0/tests/test_dbt_adapter.py +115 -0
  107. specdbt-0.1.0/tests/test_dbt_adapter_databricks.py +72 -0
  108. specdbt-0.1.0/tests/test_dbt_adapter_postgres.py +73 -0
  109. specdbt-0.1.0/tests/test_examples_jaffle_shop.py +50 -0
  110. specdbt-0.1.0/tests/test_fixtures.py +58 -0
  111. specdbt-0.1.0/tests/test_parser.py +101 -0
  112. specdbt-0.1.0/tests/test_reporter.py +54 -0
  113. specdbt-0.1.0/tests/test_runner.py +264 -0
  114. specdbt-0.1.0/tests/test_sql_literals.py +55 -0
  115. specdbt-0.1.0/tests/test_typing_utils.py +55 -0
  116. 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,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Security vulnerability
4
+ url: https://github.com/SanaVarsi/specdbt/security/advisories/new
5
+ about: Please report vulnerabilities privately, not as a public issue.
@@ -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,7 @@
1
+ ## What & why
2
+
3
+ ## Checklist
4
+
5
+ - [ ] `just test` (or `uv run pytest`) passes locally
6
+ - [ ] `pre-commit run --all-files` passes locally
7
+ - [ ] Docs/comments updated if behavior changed
@@ -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
@@ -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
@@ -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).
@@ -0,0 +1,5 @@
1
+ @AGENTS.md
2
+
3
+ Claude Code users: the `/specdbt` skill (`.claude/skills/specdbt/`) loads
4
+ the `docs/knowledge/` bundle referenced above as context on demand — use
5
+ it instead of re-reading `src/specdbt/` from scratch.
@@ -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.