continuo-python-runtime 0.4.0__tar.gz → 0.5.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 (142) hide show
  1. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/.github/workflows/ci.yml +5 -5
  2. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/.github/workflows/dco.yml +1 -1
  3. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/.github/workflows/images.yml +53 -20
  4. continuo_python_runtime-0.5.0/.github/workflows/publish-pypi.yml +99 -0
  5. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/.github/workflows/release.yml +1 -1
  6. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/.github/workflows/security.yml +4 -4
  7. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/.gitignore +1 -0
  8. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/CHANGELOG.md +46 -0
  9. continuo_python_runtime-0.5.0/CLAUDE.md +37 -0
  10. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/CONTRIBUTING.md +2 -2
  11. continuo_python_runtime-0.5.0/Dockerfile.postgres +56 -0
  12. continuo_python_runtime-0.5.0/Dockerfile.trino +56 -0
  13. continuo_python_runtime-0.4.0/README.md → continuo_python_runtime-0.5.0/PKG-INFO +78 -25
  14. continuo_python_runtime-0.4.0/PKG-INFO → continuo_python_runtime-0.5.0/README.md +58 -45
  15. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/adapters/postgres/README.md +1 -1
  16. {continuo_python_runtime-0.4.0/adapters/postgres/continuo_python_runtime_postgres → continuo_python_runtime-0.5.0/adapters/postgres/continuo_postgres_adapter}/adapter.py +1 -1
  17. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/adapters/postgres/pyproject.toml +14 -10
  18. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/adapters/postgres/tests/test_adapter_postgres_validation.py +1 -1
  19. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/adapters/postgres/tests/test_adapter_runtime_postgres.py +2 -2
  20. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/adapters/postgres/tests/test_integration_postgres_validation.py +1 -1
  21. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/adapters/postgres/tests/test_integration_runtime_postgres.py +1 -1
  22. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/adapters/trino/README.md +2 -2
  23. {continuo_python_runtime-0.4.0/adapters/trino/continuo_python_runtime_trino → continuo_python_runtime-0.5.0/adapters/trino/continuo_trino_adapter}/adapter.py +1 -1
  24. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/adapters/trino/pyproject.toml +14 -10
  25. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/adapters/trino/tests/test_adapter_runtime_trino.py +2 -2
  26. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/adapters/trino/tests/test_adapter_trino_validation.py +1 -1
  27. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/adapters/trino/tests/test_integration_runtime_trino.py +2 -2
  28. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/adapters/trino/tests/test_integration_trino_validation.py +1 -1
  29. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/validation/runner.py +21 -7
  30. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/pyproject.toml +2 -2
  31. continuo_python_runtime-0.5.0/image-requirements-postgres.txt +2 -0
  32. continuo_python_runtime-0.5.0/image-requirements-trino.txt +2 -0
  33. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/pyproject.toml +17 -8
  34. continuo_python_runtime-0.5.0/scripts/check_version_bumps.py +65 -0
  35. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/template/Dockerfile +1 -1
  36. continuo_python_runtime-0.5.0/template/Dockerfile.pip +23 -0
  37. continuo_python_runtime-0.5.0/template/README.md +82 -0
  38. continuo_python_runtime-0.5.0/template/requirements.lock +225 -0
  39. continuo_python_runtime-0.5.0/tests/test_adapter_naming.py +41 -0
  40. continuo_python_runtime-0.5.0/tests/test_image_requirements_sync.py +36 -0
  41. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_validation_runner.py +87 -7
  42. continuo_python_runtime-0.5.0/uv.lock +827 -0
  43. continuo_python_runtime-0.4.0/.github/workflows/publish-pypi.yml +0 -74
  44. continuo_python_runtime-0.4.0/Dockerfile.postgres +0 -22
  45. continuo_python_runtime-0.4.0/Dockerfile.trino +0 -22
  46. continuo_python_runtime-0.4.0/docs/superpowers/plans/2026-07-31-python-runtime.md +0 -1307
  47. continuo_python_runtime-0.4.0/docs/superpowers/plans/2026-08-07-step-3b-config-three-part-hash.md +0 -852
  48. continuo_python_runtime-0.4.0/docs/superpowers/specs/2026-07-31-python-runtime-design.md +0 -273
  49. continuo_python_runtime-0.4.0/template/README.md +0 -36
  50. continuo_python_runtime-0.4.0/uv.lock +0 -666
  51. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/.dockerignore +0 -0
  52. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/.github/CODEOWNERS +0 -0
  53. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  54. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  55. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  56. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  57. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/.github/dependabot.yml +0 -0
  58. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/.gitleaks.toml +0 -0
  59. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/CODE_OF_CONDUCT.md +0 -0
  60. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/DCO +0 -0
  61. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/LICENSE +0 -0
  62. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/NOTICE +0 -0
  63. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/SECURITY.md +0 -0
  64. {continuo_python_runtime-0.4.0/adapters/postgres/continuo_python_runtime_postgres → continuo_python_runtime-0.5.0/adapters/postgres/continuo_postgres_adapter}/__init__.py +0 -0
  65. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/adapters/postgres/tests/__init__.py +0 -0
  66. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/adapters/postgres/tests/conftest.py +0 -0
  67. {continuo_python_runtime-0.4.0/adapters/trino/continuo_python_runtime_trino → continuo_python_runtime-0.5.0/adapters/trino/continuo_trino_adapter}/__init__.py +0 -0
  68. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/adapters/trino/tests/__init__.py +0 -0
  69. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/__init__.py +0 -0
  70. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/cli.py +0 -0
  71. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/closure.py +0 -0
  72. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/conform.py +0 -0
  73. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/context.py +0 -0
  74. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/contract/__init__.py +0 -0
  75. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/contract/loader.py +0 -0
  76. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/contract/merge.py +0 -0
  77. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/contract/model.py +0 -0
  78. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/contract/paths.py +0 -0
  79. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/csv_loader.py +0 -0
  80. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/csv_readers/__init__.py +0 -0
  81. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/csv_readers/https.py +0 -0
  82. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/csv_readers/s3.py +0 -0
  83. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/csv_source.py +0 -0
  84. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/errors.py +0 -0
  85. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/harness.py +0 -0
  86. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/hashing.py +0 -0
  87. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/lint.py +0 -0
  88. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/types.py +0 -0
  89. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/validation/__init__.py +0 -0
  90. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/continuo_python_runtime/validation/s3.py +0 -0
  91. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/LICENSE +0 -0
  92. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/NOTICE +0 -0
  93. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/README.md +0 -0
  94. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/continuo_engine_contract/__init__.py +0 -0
  95. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/continuo_engine_contract/config.py +0 -0
  96. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/continuo_engine_contract/port.py +0 -0
  97. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/continuo_engine_contract/result.py +0 -0
  98. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/continuo_engine_contract/sql.py +0 -0
  99. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/continuo_engine_contract/types.py +0 -0
  100. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/tests/__init__.py +0 -0
  101. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/tests/test_config.py +0 -0
  102. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/tests/test_contract_types.py +0 -0
  103. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/tests/test_port.py +0 -0
  104. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/tests/test_result.py +0 -0
  105. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/contract/tests/test_sql.py +0 -0
  106. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/docs/boundary-contract.md +0 -0
  107. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/scripts/security-scan.sh +0 -0
  108. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/template/.github/workflows/release.yml +0 -0
  109. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/template/contracts/example.yml +0 -0
  110. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/template/contracts/example_csv.yml +0 -0
  111. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/template/scripts/example.py +0 -0
  112. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/__init__.py +0 -0
  113. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/conftest.py +0 -0
  114. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/contract/__init__.py +0 -0
  115. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/contract/test_loader.py +0 -0
  116. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/contract/test_merge.py +0 -0
  117. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/contract/test_model.py +0 -0
  118. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/smoke/node_smoke/contracts/smoke.yml +0 -0
  119. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/smoke/node_smoke/scripts/smoke.py +0 -0
  120. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/smoke/postgres-stack/docker-compose.yml +0 -0
  121. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/smoke/trino-stack/catalog/iceberg.properties +0 -0
  122. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/smoke/trino-stack/docker-compose.yml +0 -0
  123. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_cli.py +0 -0
  124. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_closure.py +0 -0
  125. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_conform.py +0 -0
  126. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_context.py +0 -0
  127. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_contract_loader.py +0 -0
  128. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_contract_pin_consistency.py +0 -0
  129. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_csv_loader.py +0 -0
  130. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_csv_readers_https.py +0 -0
  131. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_csv_readers_integration.py +0 -0
  132. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_csv_source.py +0 -0
  133. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_errors.py +0 -0
  134. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_harness.py +0 -0
  135. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_hashing.py +0 -0
  136. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_image_smoke_validation.py +0 -0
  137. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_lint.py +0 -0
  138. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_no_legacy_names.py +0 -0
  139. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_package.py +0 -0
  140. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_template.py +0 -0
  141. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_types.py +0 -0
  142. {continuo_python_runtime-0.4.0 → continuo_python_runtime-0.5.0}/tests/test_validation_s3.py +0 -0
@@ -6,7 +6,7 @@ jobs:
6
6
  test:
7
7
  runs-on: ubuntu-latest
8
8
  steps:
9
- - uses: actions/checkout@v4
9
+ - uses: actions/checkout@v7
10
10
  - uses: astral-sh/setup-uv@v7
11
11
  - name: Sync
12
12
  run: uv sync --all-packages --all-groups
@@ -19,9 +19,9 @@ jobs:
19
19
  - name: Types (contract)
20
20
  run: uv run mypy contract/continuo_engine_contract
21
21
  - name: Types (postgres adapter)
22
- run: uv run --package continuo-python-runtime-postgres mypy adapters/postgres/continuo_python_runtime_postgres
22
+ run: uv run --package continuo-postgres-adapter mypy adapters/postgres/continuo_postgres_adapter
23
23
  - name: Types (trino adapter)
24
- run: uv run --package continuo-python-runtime-trino mypy adapters/trino/continuo_python_runtime_trino
24
+ run: uv run --package continuo-trino-adapter mypy adapters/trino/continuo_trino_adapter
25
25
  # `-m "not image"` deselects tests/test_image_smoke_validation.py, which
26
26
  # needs a built engine image and the env naming it. Those tests run in
27
27
  # images.yml's smoke jobs, where an image actually exists. `and not
@@ -41,7 +41,7 @@ jobs:
41
41
  integration-postgres:
42
42
  runs-on: ubuntu-latest
43
43
  steps:
44
- - uses: actions/checkout@v4
44
+ - uses: actions/checkout@v7
45
45
  - uses: astral-sh/setup-uv@v7
46
46
  - run: uv sync --all-packages --all-groups
47
47
  - name: Start postgres stack
@@ -55,7 +55,7 @@ jobs:
55
55
  integration-trino:
56
56
  runs-on: ubuntu-latest
57
57
  steps:
58
- - uses: actions/checkout@v4
58
+ - uses: actions/checkout@v7
59
59
  - uses: astral-sh/setup-uv@v7
60
60
  - run: uv sync --all-packages --all-groups
61
61
  - name: Start trino stack
@@ -14,7 +14,7 @@ jobs:
14
14
  dco:
15
15
  runs-on: ubuntu-latest
16
16
  steps:
17
- - uses: actions/checkout@v4
17
+ - uses: actions/checkout@v7
18
18
  with:
19
19
  # The whole PR commit range has to be walkable, not just the merge commit.
20
20
  fetch-depth: 0
@@ -20,12 +20,15 @@ jobs:
20
20
  engine: [postgres, trino]
21
21
  runs-on: ubuntu-latest
22
22
  steps:
23
- - uses: actions/checkout@v4
23
+ - uses: actions/checkout@v7
24
+ - uses: astral-sh/setup-uv@v7
25
+ - name: Build wheels into a wheelhouse
26
+ run: uv build --all-packages -o wheelhouse
24
27
  - name: Build ${{ matrix.engine }} image
25
- run: docker build -f Dockerfile.${{ matrix.engine }} -t cpr-smoke-${{ matrix.engine }} .
28
+ run: docker build -f Dockerfile.${{ matrix.engine }} --build-arg WHEEL_SOURCE=wheelhouse -t cpr-smoke-${{ matrix.engine }} .
26
29
  - name: Save image
27
30
  run: docker save cpr-smoke-${{ matrix.engine }} -o /tmp/cpr-smoke-${{ matrix.engine }}.tar
28
- - uses: actions/upload-artifact@v4
31
+ - uses: actions/upload-artifact@v7
29
32
  with:
30
33
  name: cpr-smoke-${{ matrix.engine }}-image
31
34
  path: /tmp/cpr-smoke-${{ matrix.engine }}.tar
@@ -49,8 +52,8 @@ jobs:
49
52
  --health-timeout 5s
50
53
  --health-retries 10
51
54
  steps:
52
- - uses: actions/checkout@v4
53
- - uses: actions/download-artifact@v4
55
+ - uses: actions/checkout@v7
56
+ - uses: actions/download-artifact@v8
54
57
  with:
55
58
  name: cpr-smoke-postgres-image
56
59
  path: /tmp
@@ -104,8 +107,8 @@ jobs:
104
107
  needs: build
105
108
  runs-on: ubuntu-latest
106
109
  steps:
107
- - uses: actions/checkout@v4
108
- - uses: actions/download-artifact@v4
110
+ - uses: actions/checkout@v7
111
+ - uses: actions/download-artifact@v8
109
112
  with:
110
113
  name: cpr-smoke-trino-image
111
114
  path: /tmp
@@ -153,21 +156,31 @@ jobs:
153
156
  if: always()
154
157
  run: docker compose -f tests/smoke/trino-stack/docker-compose.yml down -v
155
158
 
156
- # On tag push: build both engine images for linux/amd64 and linux/arm64 and
157
- # push them to ghcr.io/<owner>/continuo-python-runtime-<engine>:<tag>. The
158
- # engine belongs in the image NAME, not the tag: Continuo's Helm chart pins an
159
- # image as `<name>:vX.Y.Z@sha256:<digest>`, so the tag must be the bare
159
+ # Runs on this same tag push, after the smoke jobs above pass. It builds both
160
+ # engine images for linux/amd64 and linux/arm64 installing the pinned
161
+ # versions FROM PyPI (WHEEL_SOURCE=pypi, the Dockerfile default), then
162
+ # pushes them to ghcr.io/<owner>/continuo-python-runtime-<engine>:<tag>. The
163
+ # engine belongs in the image NAME, not the tag: Continuo's Helm chart pins
164
+ # an image as `<name>:vX.Y.Z@sha256:<digest>`, so the tag must be the bare
160
165
  # version. Both platforms are required: domain repos build their own images
161
166
  # FROM these, and Continuo's e2e runs them in a kind cluster on aarch64
162
- # developer machines as well as on amd64 CI runners, so a single-arch publish
163
- # is unusable off amd64. The smoke jobs above stay single-arch — they load a
164
- # `docker save` tarball, which has no multi-platform equivalent.
165
- # continuo-engine-contract and the two engine adapters are installed from the
166
- # build context (see Dockerfile.postgres / Dockerfile.trino) — nothing pending
167
- # here.
167
+ # developer machines as well as on amd64 CI runners, so a single-arch
168
+ # publish is unusable off amd64. The smoke jobs above stay single-arch —
169
+ # they load a `docker save` tarball, which has no multi-platform
170
+ # equivalent.
171
+ #
172
+ # publish-pypi.yml runs in parallel on this same tag push, not before this
173
+ # job: there is no cross-workflow `needs:`, so this job cannot wait on that
174
+ # workflow directly. Instead the wait-until-installable step below polls
175
+ # PyPI until the pinned versions this tag just published are actually
176
+ # installable, which is what orders this build after publish-pypi.yml
177
+ # completes without a cross-workflow dependency.
178
+ # -test tags publish to TestPyPI only (see publish-pypi.yml), so this job
179
+ # skips them: there is no real-PyPI installable version to build an image
180
+ # from for those.
168
181
  publish:
169
- if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-test')
170
182
  needs: [smoke-postgres, smoke-trino]
183
+ if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-test')
171
184
  strategy:
172
185
  matrix:
173
186
  engine: [postgres, trino]
@@ -176,8 +189,8 @@ jobs:
176
189
  contents: read
177
190
  packages: write
178
191
  steps:
179
- - uses: actions/checkout@v4
180
- - uses: docker/setup-qemu-action@v3
192
+ - uses: actions/checkout@v7
193
+ - uses: docker/setup-qemu-action@v4
181
194
  - uses: docker/setup-buildx-action@v4
182
195
  - name: Log in to ghcr.io
183
196
  uses: docker/login-action@v4
@@ -185,6 +198,26 @@ jobs:
185
198
  registry: ghcr.io
186
199
  username: ${{ github.actor }}
187
200
  password: ${{ secrets.GITHUB_TOKEN }}
201
+ # The pinned distributions declare Requires-Python >= 3.14, so the
202
+ # availability probe must resolve them under a 3.14 interpreter — the
203
+ # runner's default python is older and pip would reject them even after
204
+ # they are published, spinning out every retry. (The image build itself
205
+ # installs inside python:3.14-slim, so it is unaffected; this is only for
206
+ # the host-side probe below.)
207
+ - uses: actions/setup-python@v7
208
+ with:
209
+ python-version: '3.14'
210
+ - name: Wait until the pinned versions are installable from PyPI
211
+ run: |
212
+ set -eu
213
+ req="image-requirements-${{ matrix.engine }}.txt"
214
+ for i in $(seq 1 30); do
215
+ if python -m pip download --dest /tmp/probe -r "$req" >/tmp/probe.log 2>&1; then
216
+ echo "installable"; exit 0
217
+ fi
218
+ echo "attempt $i: not yet on index; sleeping"; sleep 20
219
+ done
220
+ echo "pinned versions never became installable"; cat /tmp/probe.log; exit 1
188
221
  - name: Build and push ${{ matrix.engine }} image
189
222
  uses: docker/build-push-action@v7
190
223
  with:
@@ -0,0 +1,99 @@
1
+ name: publish-pypi
2
+
3
+ # Publishes all four PyPI distributions this repo owns — continuo-python-runtime
4
+ # (the harness), continuo-engine-contract (the port, result-block format, and
5
+ # shared guards), and the two engine adapters (continuo-postgres-adapter,
6
+ # continuo-trino-adapter) — via PyPI Trusted Publishing (OIDC, no stored
7
+ # token). They publish together on a single v* tag, each at its own
8
+ # pyproject version, so a tag that only bumps the runtime still carries an
9
+ # unchanged adapter version along for the ride; skip-existing (below) makes
10
+ # that a no-op rather than a duplicate-upload failure. A release-time guard
11
+ # (scripts/check_version_bumps.py) refuses a tag that changed a package's
12
+ # source without bumping its version, so skip-existing can never silently
13
+ # ship stale adapter code under an unchanged version number.
14
+ #
15
+ # Tag glob note: `v*` is the repository's single release pattern — the same tag
16
+ # that images.yml builds and pushes both engine images from, so one tag ships
17
+ # the whole release. GitHub Actions tag globs anchor at character 1, so `v*`
18
+ # claims every tag beginning with "v"; no other pattern may be introduced.
19
+ # Tag `v<ver>-test<n>` publishes to TestPyPI; `v<ver>` publishes to real PyPI.
20
+ # The GitHub environment name is what the PyPI "pending publisher" is
21
+ # registered against — all four project names need one on each index.
22
+ #
23
+ # All four distributions are built into a single `dist/` and uploaded in one
24
+ # publish call, so there is no ordering constraint between them. The runtime
25
+ # wheel declares continuo-engine-contract as a dependency and resolves it from
26
+ # the index at install time ([tool.uv.sources] is dev-only and is not embedded
27
+ # in the wheel).
28
+ on:
29
+ push:
30
+ tags:
31
+ - "v*"
32
+
33
+ permissions: {}
34
+
35
+ jobs:
36
+ publish-packages:
37
+ if: startsWith(github.ref_name, 'v')
38
+ runs-on: ubuntu-latest
39
+ environment: ${{ contains(github.ref_name, '-test') && 'testpypi' || 'pypi' }}
40
+ permissions:
41
+ id-token: write # OIDC token for Trusted Publishing
42
+ contents: read # actions/checkout needs read access to the repo
43
+ steps:
44
+ - uses: actions/checkout@v7
45
+ with:
46
+ fetch-depth: 0
47
+ - uses: astral-sh/setup-uv@v7
48
+ - name: Refuse a tag that changed a package without bumping its version
49
+ run: python scripts/check_version_bumps.py
50
+ - name: Test before publishing
51
+ # Gate all four published projects, including both adapter suites — an
52
+ # adapter regression must block its own immutable PyPI upload, and
53
+ # images.yml runs in parallel so its smoke failure cannot. Each suite is
54
+ # a SEPARATE pytest invocation: every workspace member's tests/ is its
55
+ # own `tests` package (with __init__.py + conftest.py), so running two
56
+ # of them in one process collides on the `tests.conftest` module name
57
+ # (see the [tool.pytest.ini_options] note in pyproject.toml). `not image
58
+ # and not integration` deselects the tests needing a built engine image
59
+ # or a live warehouse (images.yml / the smoke jobs cover those). The
60
+ # step's default `set -e` fails the gate if any invocation fails.
61
+ run: |
62
+ uv sync --all-packages --all-groups
63
+ uv run pytest tests contract/tests -m "not image and not integration" -q
64
+ uv run pytest adapters/postgres/tests -m "not image and not integration" -q
65
+ uv run pytest adapters/trino/tests -m "not image and not integration" -q
66
+ - name: Build the contract sdist + wheel
67
+ run: uv build --package continuo-engine-contract -o dist
68
+ - name: Build the runtime sdist + wheel
69
+ run: uv build -o dist
70
+ - name: Build the postgres adapter sdist + wheel
71
+ run: uv build --package continuo-postgres-adapter -o dist
72
+ - name: Build the trino adapter sdist + wheel
73
+ run: uv build --package continuo-trino-adapter -o dist
74
+ # Adapters change rarely, so most v* tags carry an unchanged adapter
75
+ # version alongside a bumped runtime/contract version. Without
76
+ # skip-existing, re-uploading that unchanged version 400s and fails the
77
+ # whole publish. Trade-off: a changed package published without a
78
+ # version bump is then silently skipped too — the
79
+ # check_version_bumps.py guard above is what catches that case, before
80
+ # this job's build/publish steps ever run.
81
+ - name: Publish to TestPyPI
82
+ if: contains(github.ref_name, '-test')
83
+ # Pinned to a commit SHA. release/v1 is a moving branch, and this job
84
+ # holds id-token: write for PyPI Trusted Publishing — a compromised
85
+ # revision could mint a token and publish under this project's name.
86
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
87
+ with:
88
+ repository-url: https://test.pypi.org/legacy/
89
+ packages-dir: dist
90
+ skip-existing: true
91
+ - name: Publish to PyPI
92
+ if: ${{ !contains(github.ref_name, '-test') }}
93
+ # Pinned to a commit SHA. release/v1 is a moving branch, and this job
94
+ # holds id-token: write for PyPI Trusted Publishing — a compromised
95
+ # revision could mint a token and publish under this project's name.
96
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
97
+ with:
98
+ packages-dir: dist
99
+ skip-existing: true
@@ -53,7 +53,7 @@ jobs:
53
53
  # other source of the repo's files. Its absence went unnoticed through
54
54
  # review because this job only triggers on push:tags, never on
55
55
  # pull_request, so no PR's CI ever actually executes it.
56
- - uses: actions/checkout@v4
56
+ - uses: actions/checkout@v7
57
57
 
58
58
  - name: Wait for publish-pypi.yml and images.yml to finish on this commit
59
59
  run: |
@@ -47,7 +47,7 @@ jobs:
47
47
  # range (below), and this repository is small enough (a few hundred KB,
48
48
  # well under a second to scan) that fetch-depth: 0 costs nothing on every
49
49
  # trigger rather than only on the schedule.
50
- - uses: actions/checkout@v4
50
+ - uses: actions/checkout@v7
51
51
  with:
52
52
  fetch-depth: 0
53
53
 
@@ -96,7 +96,7 @@ jobs:
96
96
  - name: Upload SARIF to code scanning
97
97
  if: always()
98
98
  continue-on-error: true
99
- uses: github/codeql-action/upload-sarif@v3
99
+ uses: github/codeql-action/upload-sarif@v4
100
100
  with:
101
101
  sarif_file: sarif/
102
102
  category: gitleaks
@@ -106,7 +106,7 @@ jobs:
106
106
  runs-on: ubuntu-latest
107
107
  timeout-minutes: 20
108
108
  steps:
109
- - uses: actions/checkout@v4
109
+ - uses: actions/checkout@v7
110
110
 
111
111
  # Advisory: reports HIGH/CRITICAL and always exits 0. Base-image and
112
112
  # transitive CVEs are often unfixable upstream, so gating on them would
@@ -124,7 +124,7 @@ jobs:
124
124
  - name: Upload SARIF to code scanning
125
125
  if: always()
126
126
  continue-on-error: true
127
- uses: github/codeql-action/upload-sarif@v3
127
+ uses: github/codeql-action/upload-sarif@v4
128
128
  with:
129
129
  sarif_file: sarif/
130
130
  category: trivy-fs
@@ -4,6 +4,7 @@ __pycache__/
4
4
  .venv/
5
5
  dist/
6
6
  .superpowers/
7
+ docs/superpowers/
7
8
  .pytest_cache/
8
9
  .mypy_cache/
9
10
  .ruff_cache/
@@ -5,6 +5,52 @@ follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.5.0] - 2026-09-09
9
+
10
+ ### Added
11
+
12
+ - `check_binds` validation op: EXPLAINs a dbt test's compiled SQL against the
13
+ candidate schema and creates nothing, so a test that names a dropped or
14
+ renamed column fails. Reads the SQL from `CANDIDATE_SQL_URI`, like
15
+ `build_from_sql`.
16
+
17
+ ### Changed
18
+
19
+ - `continuo-postgres-adapter` and `continuo-trino-adapter` 0.2.0 → 0.2.1.
20
+
21
+ ## [0.4.1] - 2026-08-25
22
+
23
+ ### Added
24
+
25
+ - Bring-your-own engine adapters: `continuo-postgres-adapter` and
26
+ `continuo-trino-adapter` are separate installable packages, so an install
27
+ can supply its own engine adapter independently of the runtime.
28
+ - `scripts/check_version_bumps.py`: fails a release when a package's source
29
+ changed since the last tag but its version did not.
30
+
31
+ ### Changed
32
+
33
+ - Deterministic python-node container builds: pinned per-engine
34
+ image-requirements and hashed template Dockerfiles.
35
+ - `continuo-engine-contract` 0.7.2 → 0.7.3.
36
+
37
+ ## [0.4.0] - 2026-08-21
38
+
39
+ ### Added
40
+
41
+ - `python-csv` node kind: a contract-only CSV loader (no user script) that
42
+ materializes the declared table from the contract's CSV URI, with a
43
+ validation header check.
44
+
45
+ ### Changed
46
+
47
+ - `continuo-engine-contract` 0.7.1 → 0.7.2.
48
+
49
+ ### Fixed
50
+
51
+ - `release.yml`'s github-release job now checks out the repository; without the
52
+ checkout it could not read the tag's files.
53
+
8
54
  ## [0.3.1] - 2026-08-21
9
55
 
10
56
  ### Added
@@ -0,0 +1,37 @@
1
+ # continuo-python-runtime
2
+
3
+ The validation/execution runtime that continuo's executor runs as a Kubernetes
4
+ Job. Python 3.12, uv workspace: the runtime (`continuo_python_runtime`), the
5
+ engine contract (`contract/`), and the engine adapters (`adapters/postgres`,
6
+ `adapters/trino`).
7
+
8
+ ## CHANGELOG is not optional
9
+
10
+ Every user-facing change keeps `CHANGELOG.md` current. Two steps, both required:
11
+
12
+ - **On every PR** that changes behavior, the CLI/op surface, a package version,
13
+ or a container: add a bullet under `## [Unreleased]` (Keep a Changelog style —
14
+ Added / Changed / Fixed / Removed). No entry, no merge.
15
+ - **On every release**: rename `## [Unreleased]` to `## [X.Y.Z] - YYYY-MM-DD`,
16
+ add a fresh empty `## [Unreleased]` above it, and tag. `release.yml` reads the
17
+ section matching the pushed tag for the GitHub Release notes, so a release with
18
+ no matching section ships with generic auto-notes and the changelog silently
19
+ loses that version.
20
+
21
+ If the changelog is missing a shipped version, backfill it from that tag's
22
+ merged PRs before adding anything new.
23
+
24
+ ## Releasing
25
+
26
+ A release is one `chore(release):` commit that bumps versions and updates the
27
+ changelog, then a `vX.Y.Z` tag on it. Bump the version of every package whose
28
+ source changed since the last tag — `scripts/check_version_bumps.py` fails the
29
+ PyPI publish otherwise. The tag builds and publishes the `-postgres` / `-trino`
30
+ images and the PyPI packages. See `CONTRIBUTING.md` for the full steps.
31
+
32
+ ## Conventions
33
+
34
+ - Standard `logging` for diagnostics, never `print` — except the CLI's
35
+ sentinel-framed result blocks, which own stdout.
36
+ - In-repo packages are pinned exactly, not with ranges (see `pyproject.toml`).
37
+ - Commits are signed off (`git commit -s`); CI enforces the DCO.
@@ -57,8 +57,8 @@ uv run ruff check .
57
57
  uv run ruff check contract
58
58
  uv run mypy continuo_python_runtime
59
59
  uv run mypy contract/continuo_engine_contract
60
- uv run --package continuo-python-runtime-postgres mypy adapters/postgres/continuo_python_runtime_postgres
61
- uv run --package continuo-python-runtime-trino mypy adapters/trino/continuo_python_runtime_trino
60
+ uv run --package continuo-postgres-adapter mypy adapters/postgres/continuo_postgres_adapter
61
+ uv run --package continuo-trino-adapter mypy adapters/trino/continuo_trino_adapter
62
62
  uv run pytest --cov=continuo_python_runtime -m "not image and not integration" -v
63
63
  uv run pytest tests/test_csv_readers_integration.py tests/test_validation_runner.py -m integration -v
64
64
  uv run pytest contract/tests -v
@@ -0,0 +1,56 @@
1
+ FROM python:3.14-slim
2
+ # One dual-role image per engine — the python-node runtime harness (default
3
+ # command `run`) and the blue/green validation runner (`validation-op`). It
4
+ # installs the PUBLISHED, versioned libraries, never the repo source:
5
+ # WHEEL_SOURCE=pypi (release) installs the pins from PyPI. PyPI versions
6
+ # are immutable and the image is consumed
7
+ # digest-pinned, so no hash lock is needed here.
8
+ # WHEEL_SOURCE=wheelhouse (CI/PR) installs the same pins from ./wheelhouse
9
+ # (built by CI) so an unreleased change is testable.
10
+ # The wheelhouse holds only this repo's own first-party wheels (built via
11
+ # `uv build --all-packages`); third-party dependencies always resolve from
12
+ # PyPI in both branches, so the wheelhouse branch never passes --no-index to
13
+ # the requirements install below.
14
+ # The pinned versions live in image-requirements-postgres.txt, kept equal to the
15
+ # repo's pyproject versions by tests/test_image_requirements_sync.py.
16
+ #
17
+ # The wheelhouse branch installs its three first-party wheels (contract,
18
+ # runtime, adapter) by exact local file first, with --no-index --no-deps: a
19
+ # plain `--find-links=/tmp/wheelhouse -r req.txt` is not enough, because pip
20
+ # does not prefer a find-links wheel over an index match at the same
21
+ # name+version, and continuo-python-runtime / continuo-engine-contract are
22
+ # already published on PyPI under the version this repo's HEAD currently
23
+ # carries (a routine dependency bump lands between releases without a version
24
+ # bump). Left unpinned-by-file, pip silently installs the last PyPI release's
25
+ # transitive pins instead of the wheel this build just produced, defeating
26
+ # the point of testing an unreleased change. Installing the exact local files
27
+ # first (no index contacted at all for this step) is unambiguous; the second
28
+ # install then only has PyPI-only third-party deps left to resolve, and finds
29
+ # the three first-party packages already satisfied.
30
+ ARG WHEEL_SOURCE=pypi
31
+ COPY image-requirements-postgres.txt /tmp/req.txt
32
+ # Optional COPY: the bracket glob matches ./wheelhouse when it exists (the CI/PR
33
+ # WHEEL_SOURCE=wheelhouse path builds it) and no-ops when it is absent, so a
34
+ # release build or a plain `docker build -f Dockerfile.postgres .` needs no
35
+ # pre-created directory. Only the wheelhouse branch below reads its contents.
36
+ COPY wheelhous[e] /tmp/wheelhouse
37
+ RUN set -eu; \
38
+ if [ "$WHEEL_SOURCE" = "wheelhouse" ]; then \
39
+ pip install --no-cache-dir --no-index --no-deps \
40
+ /tmp/wheelhouse/continuo_engine_contract-*.whl \
41
+ /tmp/wheelhouse/continuo_python_runtime-*.whl \
42
+ /tmp/wheelhouse/continuo_postgres_adapter-*.whl; \
43
+ pip install --no-cache-dir -r /tmp/req.txt; \
44
+ else \
45
+ pip install --no-cache-dir -r /tmp/req.txt; \
46
+ fi; \
47
+ rm -rf /tmp/req.txt /tmp/wheelhouse
48
+ # PYTHONPATH is belt-and-braces: the harness also inserts APP_ROOT and the
49
+ # node script's own directory at the front of sys.path before executing it.
50
+ ENV CONTRACT_DIR=/app/contracts APP_ROOT=/app PYTHONPATH=/app
51
+ WORKDIR /app
52
+ # uid 65532 matches continuo's executor securityContext expectation.
53
+ RUN useradd --uid 65532 --no-create-home --shell /usr/sbin/nologin nonroot
54
+ USER 65532:65532
55
+ ENTRYPOINT ["continuo-runtime"]
56
+ CMD ["run"]
@@ -0,0 +1,56 @@
1
+ FROM python:3.14-slim
2
+ # One dual-role image per engine — the python-node runtime harness (default
3
+ # command `run`) and the blue/green validation runner (`validation-op`). It
4
+ # installs the PUBLISHED, versioned libraries, never the repo source:
5
+ # WHEEL_SOURCE=pypi (release) installs the pins from PyPI. PyPI versions
6
+ # are immutable and the image is consumed
7
+ # digest-pinned, so no hash lock is needed here.
8
+ # WHEEL_SOURCE=wheelhouse (CI/PR) installs the same pins from ./wheelhouse
9
+ # (built by CI) so an unreleased change is testable.
10
+ # The wheelhouse holds only this repo's own first-party wheels (built via
11
+ # `uv build --all-packages`); third-party dependencies always resolve from
12
+ # PyPI in both branches, so the wheelhouse branch never passes --no-index to
13
+ # the requirements install below.
14
+ # The pinned versions live in image-requirements-trino.txt, kept equal to the
15
+ # repo's pyproject versions by tests/test_image_requirements_sync.py.
16
+ #
17
+ # The wheelhouse branch installs its three first-party wheels (contract,
18
+ # runtime, adapter) by exact local file first, with --no-index --no-deps: a
19
+ # plain `--find-links=/tmp/wheelhouse -r req.txt` is not enough, because pip
20
+ # does not prefer a find-links wheel over an index match at the same
21
+ # name+version, and continuo-python-runtime / continuo-engine-contract are
22
+ # already published on PyPI under the version this repo's HEAD currently
23
+ # carries (a routine dependency bump lands between releases without a version
24
+ # bump). Left unpinned-by-file, pip silently installs the last PyPI release's
25
+ # transitive pins instead of the wheel this build just produced, defeating
26
+ # the point of testing an unreleased change. Installing the exact local files
27
+ # first (no index contacted at all for this step) is unambiguous; the second
28
+ # install then only has PyPI-only third-party deps left to resolve, and finds
29
+ # the three first-party packages already satisfied.
30
+ ARG WHEEL_SOURCE=pypi
31
+ COPY image-requirements-trino.txt /tmp/req.txt
32
+ # Optional COPY: the bracket glob matches ./wheelhouse when it exists (the CI/PR
33
+ # WHEEL_SOURCE=wheelhouse path builds it) and no-ops when it is absent, so a
34
+ # release build or a plain `docker build -f Dockerfile.trino .` needs no
35
+ # pre-created directory. Only the wheelhouse branch below reads its contents.
36
+ COPY wheelhous[e] /tmp/wheelhouse
37
+ RUN set -eu; \
38
+ if [ "$WHEEL_SOURCE" = "wheelhouse" ]; then \
39
+ pip install --no-cache-dir --no-index --no-deps \
40
+ /tmp/wheelhouse/continuo_engine_contract-*.whl \
41
+ /tmp/wheelhouse/continuo_python_runtime-*.whl \
42
+ /tmp/wheelhouse/continuo_trino_adapter-*.whl; \
43
+ pip install --no-cache-dir -r /tmp/req.txt; \
44
+ else \
45
+ pip install --no-cache-dir -r /tmp/req.txt; \
46
+ fi; \
47
+ rm -rf /tmp/req.txt /tmp/wheelhouse
48
+ # PYTHONPATH is belt-and-braces: the harness also inserts APP_ROOT and the
49
+ # node script's own directory at the front of sys.path before executing it.
50
+ ENV CONTRACT_DIR=/app/contracts APP_ROOT=/app PYTHONPATH=/app
51
+ WORKDIR /app
52
+ # uid 65532 matches continuo's executor securityContext expectation.
53
+ RUN useradd --uid 65532 --no-create-home --shell /usr/sbin/nologin nonroot
54
+ USER 65532:65532
55
+ ENTRYPOINT ["continuo-runtime"]
56
+ CMD ["run"]