continuo-python-runtime 0.3.0__tar.gz → 0.4.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 (129) hide show
  1. continuo_python_runtime-0.4.0/.github/CODEOWNERS +2 -0
  2. continuo_python_runtime-0.4.0/.github/ISSUE_TEMPLATE/bug_report.yml +59 -0
  3. continuo_python_runtime-0.4.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  4. continuo_python_runtime-0.4.0/.github/ISSUE_TEMPLATE/feature_request.yml +30 -0
  5. continuo_python_runtime-0.4.0/.github/PULL_REQUEST_TEMPLATE.md +20 -0
  6. continuo_python_runtime-0.4.0/.github/dependabot.yml +64 -0
  7. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/.github/workflows/ci.yml +11 -5
  8. continuo_python_runtime-0.4.0/.github/workflows/dco.yml +49 -0
  9. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/.github/workflows/images.yml +5 -5
  10. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/.github/workflows/publish-pypi.yml +1 -1
  11. continuo_python_runtime-0.4.0/.github/workflows/release.yml +147 -0
  12. continuo_python_runtime-0.4.0/.github/workflows/security.yml +130 -0
  13. continuo_python_runtime-0.4.0/.gitleaks.toml +36 -0
  14. continuo_python_runtime-0.4.0/CHANGELOG.md +84 -0
  15. continuo_python_runtime-0.4.0/CODE_OF_CONDUCT.md +133 -0
  16. continuo_python_runtime-0.4.0/CONTRIBUTING.md +102 -0
  17. continuo_python_runtime-0.4.0/DCO +34 -0
  18. continuo_python_runtime-0.4.0/LICENSE +201 -0
  19. continuo_python_runtime-0.4.0/NOTICE +11 -0
  20. continuo_python_runtime-0.3.0/README.md → continuo_python_runtime-0.4.0/PKG-INFO +53 -3
  21. continuo_python_runtime-0.3.0/PKG-INFO → continuo_python_runtime-0.4.0/README.md +33 -20
  22. continuo_python_runtime-0.4.0/SECURITY.md +80 -0
  23. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/postgres/pyproject.toml +2 -2
  24. continuo_python_runtime-0.4.0/adapters/postgres/tests/conftest.py +114 -0
  25. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/postgres/tests/test_integration_runtime_postgres.py +79 -0
  26. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/trino/pyproject.toml +2 -2
  27. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/contract/loader.py +66 -31
  28. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/contract/merge.py +14 -6
  29. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/contract/model.py +2 -0
  30. continuo_python_runtime-0.4.0/continuo_python_runtime/csv_loader.py +68 -0
  31. continuo_python_runtime-0.4.0/continuo_python_runtime/csv_readers/__init__.py +17 -0
  32. continuo_python_runtime-0.4.0/continuo_python_runtime/csv_readers/https.py +118 -0
  33. continuo_python_runtime-0.4.0/continuo_python_runtime/csv_readers/s3.py +57 -0
  34. continuo_python_runtime-0.4.0/continuo_python_runtime/csv_source.py +96 -0
  35. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/harness.py +18 -7
  36. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/validation/runner.py +39 -4
  37. continuo_python_runtime-0.4.0/contract/LICENSE +201 -0
  38. continuo_python_runtime-0.4.0/contract/NOTICE +12 -0
  39. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/contract/pyproject.toml +3 -1
  40. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/docs/boundary-contract.md +24 -1
  41. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/pyproject.toml +5 -3
  42. continuo_python_runtime-0.4.0/scripts/security-scan.sh +178 -0
  43. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/template/Dockerfile +1 -1
  44. continuo_python_runtime-0.4.0/template/contracts/example_csv.yml +17 -0
  45. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/conftest.py +97 -0
  46. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/contract/test_loader.py +59 -0
  47. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/contract/test_merge.py +54 -1
  48. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/contract/test_model.py +15 -1
  49. continuo_python_runtime-0.4.0/tests/test_contract_loader.py +32 -0
  50. continuo_python_runtime-0.4.0/tests/test_csv_loader.py +116 -0
  51. continuo_python_runtime-0.4.0/tests/test_csv_readers_https.py +107 -0
  52. continuo_python_runtime-0.4.0/tests/test_csv_readers_integration.py +302 -0
  53. continuo_python_runtime-0.4.0/tests/test_csv_source.py +64 -0
  54. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_harness.py +54 -0
  55. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_no_legacy_names.py +4 -1
  56. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_template.py +9 -5
  57. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_validation_runner.py +206 -4
  58. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/uv.lock +9 -8
  59. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/.dockerignore +0 -0
  60. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/.gitignore +0 -0
  61. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/Dockerfile.postgres +0 -0
  62. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/Dockerfile.trino +0 -0
  63. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/postgres/README.md +0 -0
  64. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/postgres/continuo_python_runtime_postgres/__init__.py +0 -0
  65. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/postgres/continuo_python_runtime_postgres/adapter.py +0 -0
  66. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/postgres/tests/__init__.py +0 -0
  67. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/postgres/tests/test_adapter_postgres_validation.py +0 -0
  68. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/postgres/tests/test_adapter_runtime_postgres.py +0 -0
  69. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/postgres/tests/test_integration_postgres_validation.py +0 -0
  70. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/trino/README.md +0 -0
  71. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/trino/continuo_python_runtime_trino/__init__.py +0 -0
  72. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/trino/continuo_python_runtime_trino/adapter.py +0 -0
  73. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/trino/tests/__init__.py +0 -0
  74. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/trino/tests/test_adapter_runtime_trino.py +0 -0
  75. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/trino/tests/test_adapter_trino_validation.py +0 -0
  76. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/trino/tests/test_integration_runtime_trino.py +0 -0
  77. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/adapters/trino/tests/test_integration_trino_validation.py +0 -0
  78. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/__init__.py +0 -0
  79. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/cli.py +0 -0
  80. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/closure.py +0 -0
  81. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/conform.py +0 -0
  82. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/context.py +0 -0
  83. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/contract/__init__.py +0 -0
  84. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/contract/paths.py +0 -0
  85. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/errors.py +0 -0
  86. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/hashing.py +0 -0
  87. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/lint.py +0 -0
  88. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/types.py +0 -0
  89. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/validation/__init__.py +0 -0
  90. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/continuo_python_runtime/validation/s3.py +0 -0
  91. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/contract/README.md +0 -0
  92. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/contract/continuo_engine_contract/__init__.py +0 -0
  93. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/contract/continuo_engine_contract/config.py +0 -0
  94. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/contract/continuo_engine_contract/port.py +0 -0
  95. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/contract/continuo_engine_contract/result.py +0 -0
  96. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/contract/continuo_engine_contract/sql.py +0 -0
  97. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/contract/continuo_engine_contract/types.py +0 -0
  98. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/contract/tests/__init__.py +0 -0
  99. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/contract/tests/test_config.py +0 -0
  100. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/contract/tests/test_contract_types.py +0 -0
  101. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/contract/tests/test_port.py +0 -0
  102. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/contract/tests/test_result.py +0 -0
  103. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/contract/tests/test_sql.py +0 -0
  104. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/docs/superpowers/plans/2026-07-31-python-runtime.md +0 -0
  105. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/docs/superpowers/plans/2026-08-07-step-3b-config-three-part-hash.md +0 -0
  106. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/docs/superpowers/specs/2026-07-31-python-runtime-design.md +0 -0
  107. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/template/.github/workflows/release.yml +0 -0
  108. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/template/README.md +0 -0
  109. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/template/contracts/example.yml +0 -0
  110. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/template/scripts/example.py +0 -0
  111. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/__init__.py +0 -0
  112. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/contract/__init__.py +0 -0
  113. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/smoke/node_smoke/contracts/smoke.yml +0 -0
  114. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/smoke/node_smoke/scripts/smoke.py +0 -0
  115. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/smoke/postgres-stack/docker-compose.yml +0 -0
  116. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/smoke/trino-stack/catalog/iceberg.properties +0 -0
  117. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/smoke/trino-stack/docker-compose.yml +0 -0
  118. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_cli.py +0 -0
  119. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_closure.py +0 -0
  120. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_conform.py +0 -0
  121. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_context.py +0 -0
  122. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_contract_pin_consistency.py +0 -0
  123. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_errors.py +0 -0
  124. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_hashing.py +0 -0
  125. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_image_smoke_validation.py +0 -0
  126. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_lint.py +0 -0
  127. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_package.py +0 -0
  128. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_types.py +0 -0
  129. {continuo_python_runtime-0.3.0 → continuo_python_runtime-0.4.0}/tests/test_validation_s3.py +0 -0
@@ -0,0 +1,2 @@
1
+ # Every change is reviewed by the maintainer.
2
+ * @carolsimone
@@ -0,0 +1,59 @@
1
+ name: Bug report
2
+ description: Something in the Continuo Python runtime behaves incorrectly.
3
+ labels: ["bug"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Please do not report security vulnerabilities here — see SECURITY.md.
9
+
10
+ - type: textarea
11
+ id: what-happened
12
+ attributes:
13
+ label: What happened
14
+ description: What did you observe, and what did you expect instead?
15
+ validations:
16
+ required: true
17
+
18
+ - type: textarea
19
+ id: reproduce
20
+ attributes:
21
+ label: Steps to reproduce
22
+ placeholder: |
23
+ 1. Write a contract with ...
24
+ 2. Run `continuo-runtime validate ...`
25
+ 3. Observe ...
26
+ validations:
27
+ required: true
28
+
29
+ - type: dropdown
30
+ id: component
31
+ attributes:
32
+ label: Which component
33
+ multiple: true
34
+ options:
35
+ - continuo_python_runtime (harness / CLI)
36
+ - contract (continuo-engine-contract)
37
+ - adapters/postgres
38
+ - adapters/trino
39
+ - template
40
+ - CI (.github/workflows)
41
+ - Not sure
42
+ validations:
43
+ required: true
44
+
45
+ - type: input
46
+ id: version
47
+ attributes:
48
+ label: Version
49
+ description: Package version (PyPI) or commit SHA.
50
+ placeholder: "v0.3.0"
51
+ validations:
52
+ required: true
53
+
54
+ - type: textarea
55
+ id: logs
56
+ attributes:
57
+ label: Relevant logs
58
+ description: Please redact any credentials before pasting.
59
+ render: shell
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Security vulnerability
4
+ url: https://github.com/carolsimone/continuo-python-runtime/security/advisories/new
5
+ about: Report privately. Please do not open a public issue for security problems.
@@ -0,0 +1,30 @@
1
+ name: Feature request
2
+ description: Propose a change or addition to the Continuo Python runtime.
3
+ labels: ["enhancement"]
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: What problem does this solve
9
+ description: Describe the situation you are in, not the solution you have in mind.
10
+ validations:
11
+ required: true
12
+
13
+ - type: textarea
14
+ id: proposal
15
+ attributes:
16
+ label: What you would like to happen
17
+ validations:
18
+ required: true
19
+
20
+ - type: textarea
21
+ id: alternatives
22
+ attributes:
23
+ label: Alternatives you considered
24
+
25
+ - type: checkboxes
26
+ id: contribute
27
+ attributes:
28
+ label: Contribution
29
+ options:
30
+ - label: I am willing to open a pull request for this.
@@ -0,0 +1,20 @@
1
+ ## What this changes
2
+
3
+ <!-- One or two sentences. Link the issue this addresses. -->
4
+
5
+ Closes #
6
+
7
+ ## Why
8
+
9
+ <!-- What problem does this solve? -->
10
+
11
+ ## How it was verified
12
+
13
+ <!-- The commands you ran and what they printed. "Tests pass" on its own is not enough. -->
14
+
15
+ ## Checklist
16
+
17
+ - [ ] Every commit is signed off (`git commit -s`) — the `dco` check enforces this
18
+ - [ ] Tests added or updated for the behavior changed
19
+ - [ ] `uv run ruff check .` and `uv run mypy` (relevant packages) pass
20
+ - [ ] `scripts/security-scan.sh` passes (dependency or credential-adjacent changes)
@@ -0,0 +1,64 @@
1
+ # Dependabot keeps dependencies current so that known CVEs are fixed by an
2
+ # upgrade before a scanner has to report them.
3
+ #
4
+ # Every ecosystem groups its patch and minor updates into a single pull request
5
+ # per week to keep the noise down. Major updates stay ungrouped because they
6
+ # carry breaking changes and need to be reviewed one at a time.
7
+ version: 2
8
+
9
+ updates:
10
+ # The uv workspace: this package (root), contract/, and the two adapters. All
11
+ # four share one lockfile, but Dependabot still needs one entry per pyproject.toml
12
+ # directory to see each package's own dependency declarations.
13
+ - package-ecosystem: uv
14
+ directories:
15
+ - /
16
+ - /contract
17
+ - /adapters/postgres
18
+ - /adapters/trino
19
+ schedule:
20
+ interval: weekly
21
+ open-pull-requests-limit: 5
22
+ groups:
23
+ python-minor-patch:
24
+ applies-to: version-updates
25
+ update-types: [minor, patch]
26
+ python-security:
27
+ applies-to: security-updates
28
+ patterns: ["*"]
29
+ commit-message:
30
+ prefix: chore(deps)
31
+
32
+ # Actions are a supply-chain surface in their own right: a compromised or
33
+ # abandoned action runs with access to the workflow token.
34
+ - package-ecosystem: github-actions
35
+ directory: /
36
+ schedule:
37
+ interval: weekly
38
+ open-pull-requests-limit: 5
39
+ groups:
40
+ actions:
41
+ applies-to: version-updates
42
+ update-types: [minor, patch]
43
+ actions-security:
44
+ applies-to: security-updates
45
+ patterns: ["*"]
46
+ commit-message:
47
+ prefix: chore(ci)
48
+
49
+ # Dockerfile.postgres and Dockerfile.trino, the published engine images. Most
50
+ # base-image CVEs are fixed by moving to a newer tag.
51
+ - package-ecosystem: docker
52
+ directory: /
53
+ schedule:
54
+ interval: weekly
55
+ open-pull-requests-limit: 5
56
+ groups:
57
+ docker:
58
+ applies-to: version-updates
59
+ update-types: [minor, patch]
60
+ docker-security:
61
+ applies-to: security-updates
62
+ patterns: ["*"]
63
+ commit-message:
64
+ prefix: chore(deps)
@@ -7,7 +7,7 @@ jobs:
7
7
  runs-on: ubuntu-latest
8
8
  steps:
9
9
  - uses: actions/checkout@v4
10
- - uses: astral-sh/setup-uv@v5
10
+ - uses: astral-sh/setup-uv@v7
11
11
  - name: Sync
12
12
  run: uv sync --all-packages --all-groups
13
13
  - name: Lint
@@ -24,9 +24,15 @@ jobs:
24
24
  run: uv run --package continuo-python-runtime-trino mypy adapters/trino/continuo_python_runtime_trino
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
- # images.yml's smoke jobs, where an image actually exists.
27
+ # images.yml's smoke jobs, where an image actually exists. `and not
28
+ # integration` deselects the csv-reader/validation-runner tests that
29
+ # need a real minio backend (started via `docker run` by the
30
+ # `minio_container` fixture, not docker-compose) -- those run in the
31
+ # dedicated step below, on the same runner, where docker is available.
28
32
  - name: Tests (runtime)
29
- run: uv run pytest --cov=continuo_python_runtime -m "not image" -v
33
+ run: uv run pytest --cov=continuo_python_runtime -m "not image and not integration" -v
34
+ - name: Tests (runtime, integration)
35
+ run: uv run pytest tests/test_csv_readers_integration.py tests/test_validation_runner.py -m integration -v
30
36
  - name: Tests (contract)
31
37
  run: uv run pytest contract/tests -v
32
38
  - name: Tests (adapter units)
@@ -36,7 +42,7 @@ jobs:
36
42
  runs-on: ubuntu-latest
37
43
  steps:
38
44
  - uses: actions/checkout@v4
39
- - uses: astral-sh/setup-uv@v5
45
+ - uses: astral-sh/setup-uv@v7
40
46
  - run: uv sync --all-packages --all-groups
41
47
  - name: Start postgres stack
42
48
  run: docker compose -f tests/smoke/postgres-stack/docker-compose.yml up -d --wait
@@ -50,7 +56,7 @@ jobs:
50
56
  runs-on: ubuntu-latest
51
57
  steps:
52
58
  - uses: actions/checkout@v4
53
- - uses: astral-sh/setup-uv@v5
59
+ - uses: astral-sh/setup-uv@v7
54
60
  - run: uv sync --all-packages --all-groups
55
61
  - name: Start trino stack
56
62
  run: docker compose -f tests/smoke/trino-stack/docker-compose.yml up -d --wait
@@ -0,0 +1,49 @@
1
+ name: dco
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - main
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ # Job name becomes the check-run name, which is what a branch ruleset requires
13
+ # by string. Keep it "dco" so the required check is unambiguous.
14
+ dco:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ with:
19
+ # The whole PR commit range has to be walkable, not just the merge commit.
20
+ fetch-depth: 0
21
+
22
+ - name: Verify every commit is signed off
23
+ env:
24
+ BASE_SHA: ${{ github.event.pull_request.base.sha }}
25
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
26
+ run: |
27
+ set -euo pipefail
28
+ missing=0
29
+ while read -r sha; do
30
+ [ -z "$sha" ] && continue
31
+ # Merge commits are generated by GitHub and carry no sign-off.
32
+ if [ "$(git rev-list --no-walk --count --merges "$sha")" -eq 1 ]; then
33
+ continue
34
+ fi
35
+ if ! git show -s --format='%B' "$sha" | grep -qiE '^Signed-off-by: .+ <.+@.+>$'; then
36
+ echo "::error::Commit $sha ($(git show -s --format='%an' "$sha")) has no Signed-off-by line."
37
+ missing=1
38
+ fi
39
+ done < <(git rev-list "$BASE_SHA".."$HEAD_SHA")
40
+
41
+ if [ "$missing" -ne 0 ]; then
42
+ echo ""
43
+ echo "Every commit needs a Developer Certificate of Origin sign-off (see DCO)."
44
+ echo "Fix an existing branch with:"
45
+ echo " git rebase --signoff origin/main && git push --force-with-lease"
46
+ echo "Sign off future commits automatically with 'git commit -s'."
47
+ exit 1
48
+ fi
49
+ echo "All commits signed off."
@@ -56,7 +56,7 @@ jobs:
56
56
  path: /tmp
57
57
  - name: Load image
58
58
  run: docker load -i /tmp/cpr-smoke-postgres.tar
59
- - uses: astral-sh/setup-uv@v5
59
+ - uses: astral-sh/setup-uv@v7
60
60
  - name: Sync
61
61
  run: uv sync --all-packages --all-groups
62
62
  - name: Image smoke tests (postgres)
@@ -111,7 +111,7 @@ jobs:
111
111
  path: /tmp
112
112
  - name: Load image
113
113
  run: docker load -i /tmp/cpr-smoke-trino.tar
114
- - uses: astral-sh/setup-uv@v5
114
+ - uses: astral-sh/setup-uv@v7
115
115
  - name: Sync
116
116
  run: uv sync --all-packages --all-groups
117
117
  - name: Image smoke tests (trino)
@@ -178,15 +178,15 @@ jobs:
178
178
  steps:
179
179
  - uses: actions/checkout@v4
180
180
  - uses: docker/setup-qemu-action@v3
181
- - uses: docker/setup-buildx-action@v3
181
+ - uses: docker/setup-buildx-action@v4
182
182
  - name: Log in to ghcr.io
183
- uses: docker/login-action@v3
183
+ uses: docker/login-action@v4
184
184
  with:
185
185
  registry: ghcr.io
186
186
  username: ${{ github.actor }}
187
187
  password: ${{ secrets.GITHUB_TOKEN }}
188
188
  - name: Build and push ${{ matrix.engine }} image
189
- uses: docker/build-push-action@v6
189
+ uses: docker/build-push-action@v7
190
190
  with:
191
191
  context: .
192
192
  file: Dockerfile.${{ matrix.engine }}
@@ -44,7 +44,7 @@ jobs:
44
44
  contents: read # actions/checkout needs read access to the repo
45
45
  steps:
46
46
  - uses: actions/checkout@v4
47
- - uses: astral-sh/setup-uv@v5
47
+ - uses: astral-sh/setup-uv@v7
48
48
  - name: Test before publishing
49
49
  # `-m "not image"` deselects the image smoke tests; images.yml runs
50
50
  # those against a built engine image.
@@ -0,0 +1,147 @@
1
+ name: release
2
+
3
+ # Creates a GitHub Release once BOTH tag-triggered publish workflows
4
+ # (publish-pypi.yml, images.yml) have finished successfully for this exact
5
+ # tag. A Release exists here beyond what PyPI already shows because a tag
6
+ # also ships two ghcr.io images that PyPI knows nothing about — the Release
7
+ # is the one place that says "this tag = these packages + these images."
8
+ #
9
+ # Triggered by the same tag push as its two siblings, rather than by
10
+ # workflow_run watching them complete: `needs:` cannot cross workflow files,
11
+ # and this avoids depending on workflow_run's head_branch semantics for a
12
+ # tag-triggered source run, which nothing else in this repo relies on.
13
+ # Instead the job below polls the Actions API for the sibling runs at this
14
+ # exact commit until both report a conclusion — that only depends on the
15
+ # same push:tags trigger every other release workflow here already uses.
16
+ on:
17
+ push:
18
+ tags:
19
+ # Digit after v: keeps this to release tags, same reasoning continuo's
20
+ # release.yml uses. publish-pypi.yml and images.yml use the looser `v*`
21
+ # and instead guard internally with startsWith(..., 'v') checks, so
22
+ # this is deliberately the stricter of the three triggers.
23
+ - "v[0-9]*"
24
+
25
+ concurrency:
26
+ group: release-${{ github.ref }}
27
+ cancel-in-progress: false
28
+
29
+ permissions:
30
+ contents: read
31
+
32
+ jobs:
33
+ github-release:
34
+ # -test tags are a TestPyPI dry run (see publish-pypi.yml) and skip
35
+ # images.yml's publish job by design (its own if: guards on this same
36
+ # condition) — no public Release for them.
37
+ if: "!contains(github.ref_name, '-test')"
38
+ runs-on: ubuntu-latest
39
+ # Each wait_for call below can itself run up to 30 minutes (its own
40
+ # internal timeout), called twice, sequentially — so this job's ceiling
41
+ # has to clear 60 minutes on its own polling math alone, before even
42
+ # accounting for how long the siblings actually take to run.
43
+ timeout-minutes: 70
44
+ permissions:
45
+ contents: write # gh release create/edit
46
+ actions: read # poll sibling workflow runs
47
+ env:
48
+ TAG: ${{ github.ref_name }}
49
+ SHA: ${{ github.sha }}
50
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
51
+ steps:
52
+ # Needed for the CHANGELOG.md extraction step below — this job has no
53
+ # other source of the repo's files. Its absence went unnoticed through
54
+ # review because this job only triggers on push:tags, never on
55
+ # pull_request, so no PR's CI ever actually executes it.
56
+ - uses: actions/checkout@v4
57
+
58
+ - name: Wait for publish-pypi.yml and images.yml to finish on this commit
59
+ run: |
60
+ set -euo pipefail
61
+
62
+ wait_for() {
63
+ local workflow_file="$1" elapsed=0 interval=20 timeout=1800
64
+ while :; do
65
+ # event=push, not just head_sha: images.yml also triggers on
66
+ # pull_request, and a coincidentally-identical head_sha between
67
+ # an open PR and this tag would otherwise pick up the wrong run.
68
+ # Query params go in the URL, not via -f: gh api switches a
69
+ # request to POST the moment any -f/-F flag is present, and this
70
+ # listing endpoint only accepts GET — it 404s on POST, which
71
+ # under `set -e` would abort this script on the very first poll,
72
+ # every time. Verified against this repo's real API: -f flags
73
+ # 404 here, the URL query string form returns real run data.
74
+ conclusion="$(gh api "repos/${GITHUB_REPOSITORY}/actions/workflows/${workflow_file}/runs?head_sha=${SHA}&event=push" \
75
+ --jq '.workflow_runs[0].conclusion // "pending"')"
76
+ case "${conclusion}" in
77
+ success)
78
+ echo "${workflow_file}: success"
79
+ return 0
80
+ ;;
81
+ pending)
82
+ # Covers both "no matching run yet" (API eventual
83
+ # consistency right after the push) and "run exists but
84
+ # still queued/in_progress" — conclusion is JSON null in
85
+ # both cases until the run completes.
86
+ ;;
87
+ *)
88
+ echo "::error::${workflow_file} concluded '${conclusion}' for ${SHA}; not creating a Release."
89
+ exit 1
90
+ ;;
91
+ esac
92
+ elapsed=$((elapsed + interval))
93
+ if [ "${elapsed}" -ge "${timeout}" ]; then
94
+ echo "::error::timed out after ${timeout}s waiting for ${workflow_file} to finish on ${SHA}."
95
+ exit 1
96
+ fi
97
+ sleep "${interval}"
98
+ done
99
+ }
100
+
101
+ wait_for "publish-pypi.yml"
102
+ wait_for "images.yml"
103
+
104
+ - name: Extract this version's CHANGELOG section
105
+ id: notes
106
+ run: |
107
+ set -euo pipefail
108
+ VERSION="${TAG#v}"
109
+ # Print the body between "## [X.Y.Z]" and the next "## [" heading.
110
+ if grep -qE "^## \[${VERSION}\]" CHANGELOG.md; then
111
+ awk -v ver="## [${VERSION}]" '
112
+ index($0, ver) == 1 { found = 1; next }
113
+ found && /^## \[/ { exit }
114
+ found { print }
115
+ ' CHANGELOG.md > /tmp/release-body.md
116
+ fi
117
+
118
+ if [ -s /tmp/release-body.md ]; then
119
+ echo "generate=false" >> "$GITHUB_OUTPUT"
120
+ else
121
+ # A tag with no matching CHANGELOG.md section (e.g. someone
122
+ # forgot to add one before tagging) still gets a Release — just
123
+ # with GitHub's generated notes instead of failing the job over
124
+ # a missing doc update.
125
+ echo "::warning::no '## [${VERSION}]' section in CHANGELOG.md; falling back to generated notes."
126
+ echo "generate=true" >> "$GITHUB_OUTPUT"
127
+ fi
128
+
129
+ - name: Create the Release
130
+ run: |
131
+ set -euo pipefail
132
+ # Idempotent: re-running this workflow for an existing tag updates
133
+ # the Release rather than failing.
134
+ if gh release view "${TAG}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then
135
+ if [ "${{ steps.notes.outputs.generate }}" = "true" ]; then
136
+ gh release edit "${TAG}" --repo "${GITHUB_REPOSITORY}" --latest
137
+ else
138
+ gh release edit "${TAG}" --repo "${GITHUB_REPOSITORY}" \
139
+ --notes-file /tmp/release-body.md --latest
140
+ fi
141
+ elif [ "${{ steps.notes.outputs.generate }}" = "true" ]; then
142
+ gh release create "${TAG}" --repo "${GITHUB_REPOSITORY}" \
143
+ --title "${TAG}" --generate-notes --latest
144
+ else
145
+ gh release create "${TAG}" --repo "${GITHUB_REPOSITORY}" \
146
+ --title "${TAG}" --notes-file /tmp/release-body.md --latest
147
+ fi
@@ -0,0 +1,130 @@
1
+ name: security
2
+
3
+ # Deliberately separate from ci.yml so a scanner failure or the weekly schedule
4
+ # never blocks or piggybacks on the test/lint pipeline.
5
+ on:
6
+ pull_request:
7
+ branches:
8
+ - main
9
+ push:
10
+ branches:
11
+ - main
12
+ schedule:
13
+ # Weekly, Monday 04:00 UTC. The scheduled run is the point of this workflow:
14
+ # most vulnerabilities are disclosed after the code that depends on them was
15
+ # last touched, so a pull-request-only trigger never sees them.
16
+ - cron: "0 4 * * 1"
17
+
18
+ concurrency:
19
+ # Keyed by event as well as ref. A schedule event and a push to main both carry
20
+ # github.ref == refs/heads/main, so keying on ref alone would put them in one
21
+ # group and let a routine merge, via cancel-in-progress, abort the weekly
22
+ # full-history secret scan — the run this workflow exists for. Including
23
+ # github.event_name isolates the scheduled run while still superseding stale
24
+ # per-PR runs (each PR has a distinct ref).
25
+ group: security-${{ github.ref }}-${{ github.event_name }}
26
+ cancel-in-progress: true
27
+
28
+ # Each job uploads its findings as SARIF, so they land in Security > Code scanning
29
+ # instead of scrolling away in a job log. security-events: write is what permits
30
+ # that upload; contents: read has to be restated because a permissions block
31
+ # replaces the default set rather than adding to it.
32
+ #
33
+ # Note the Security tab will show findings the blocking gate deliberately
34
+ # ignores: the Trivy dependency scan is advisory by design. A green pipeline
35
+ # with open alerts is the intended state, not a contradiction.
36
+ permissions:
37
+ contents: read
38
+ security-events: write
39
+
40
+ jobs:
41
+ secrets:
42
+ name: Secret scan (blocking)
43
+ runs-on: ubuntu-latest
44
+ timeout-minutes: 15
45
+ steps:
46
+ # Always full history: pull_request and push both need to walk a commit
47
+ # range (below), and this repository is small enough (a few hundred KB,
48
+ # well under a second to scan) that fetch-depth: 0 costs nothing on every
49
+ # trigger rather than only on the schedule.
50
+ - uses: actions/checkout@v4
51
+ with:
52
+ fetch-depth: 0
53
+
54
+ # pull_request scans exactly the commits the PR introduces (base..head).
55
+ # A `dir` scan of the final working tree would miss a secret added in one
56
+ # commit and removed in a later one within the same PR — the working tree
57
+ # never carries it, but a history-preserving merge still makes that commit
58
+ # reachable from main. push scans the commits a direct push to main added
59
+ # (before..after), for the same reason. The weekly schedule walks every
60
+ # commit reachable from HEAD, catching anything that entered the
61
+ # repository before this gate existed.
62
+ - name: Run gitleaks
63
+ env:
64
+ SARIF_DIR: sarif
65
+ run: |
66
+ case "${{ github.event_name }}" in
67
+ schedule)
68
+ bash scripts/security-scan.sh secrets --history
69
+ ;;
70
+ pull_request)
71
+ bash scripts/security-scan.sh secrets --range \
72
+ "${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}"
73
+ ;;
74
+ push)
75
+ before="${{ github.event.before }}"
76
+ # A zero SHA means there is no prior commit to diff from — a new
77
+ # branch's first push, or history rewritten out from under this
78
+ # ref. Neither has a meaningful range, so fall back to scanning
79
+ # just the commit that was pushed.
80
+ if [ "${before}" = "0000000000000000000000000000000000000000" ]; then
81
+ before="${{ github.event.after }}^"
82
+ fi
83
+ bash scripts/security-scan.sh secrets --range "${before}..${{ github.event.after }}"
84
+ ;;
85
+ esac
86
+ # if: always() — a blocking scan that fails must still publish what it
87
+ # found, otherwise the findings that justify the failure never appear.
88
+ #
89
+ # continue-on-error: true — code scanning needs GitHub Advanced Security,
90
+ # free on a public repo but not on a private one. This repo is private
91
+ # until the open-sourcing work lands, so the upload 403s
92
+ # ("Resource not accessible by integration") on every run until then.
93
+ # The upload is reporting, not the gate; gitleaks failing the step above
94
+ # is what actually blocks a merge, and must keep doing so independent of
95
+ # whether the upload succeeds.
96
+ - name: Upload SARIF to code scanning
97
+ if: always()
98
+ continue-on-error: true
99
+ uses: github/codeql-action/upload-sarif@v3
100
+ with:
101
+ sarif_file: sarif/
102
+ category: gitleaks
103
+
104
+ dependencies:
105
+ name: Dependency CVEs (advisory)
106
+ runs-on: ubuntu-latest
107
+ timeout-minutes: 20
108
+ steps:
109
+ - uses: actions/checkout@v4
110
+
111
+ # Advisory: reports HIGH/CRITICAL and always exits 0. Base-image and
112
+ # transitive CVEs are often unfixable upstream, so gating on them would
113
+ # block every merge behind an ignore-list edit. Dependabot is what actually
114
+ # resolves these; this job exists to make them visible.
115
+ - name: Run Trivy dependency scan
116
+ env:
117
+ SARIF_DIR: sarif
118
+ run: bash scripts/security-scan.sh deps
119
+ # if: always() — a blocking scan that fails must still publish what it
120
+ # found, otherwise the findings that justify the failure never appear.
121
+ #
122
+ # continue-on-error: true — see the identical note on the secrets job;
123
+ # same GHAS-requires-public-repo cause, same fix.
124
+ - name: Upload SARIF to code scanning
125
+ if: always()
126
+ continue-on-error: true
127
+ uses: github/codeql-action/upload-sarif@v3
128
+ with:
129
+ sarif_file: sarif/
130
+ category: trivy-fs
@@ -0,0 +1,36 @@
1
+ # gitleaks configuration.
2
+ #
3
+ # Extends the upstream default ruleset rather than replacing it, so rules added
4
+ # upstream apply automatically. Only repository-specific exemptions live here.
5
+
6
+ [extend]
7
+ useDefault = true
8
+
9
+ [allowlist]
10
+ description = "Paths that contain no live credentials"
11
+
12
+ paths = [
13
+ # Unanchored throughout: the working-tree scan (scripts/security-scan.sh)
14
+ # mounts the repo at /repo and passes that as the scan target, so gitleaks
15
+ # matches these patterns against absolute paths like
16
+ # /repo/.worktrees/stale/file — a leading `^` would never match the actual
17
+ # prefix and silently disable the exclusion. Verified against v8.24.3 by
18
+ # planting a fake AWS key under .worktrees/ and confirming a start-anchored
19
+ # pattern let it through while an unanchored one catches it.
20
+ #
21
+ # Local agent worktrees hold full stale copies of every tracked file, so a
22
+ # finding in the real tree would otherwise be reported once per worktree.
23
+ '''(^|/)\.worktrees/''',
24
+ '''(^|/)\.claude/''',
25
+ # Build/tooling caches, never committed.
26
+ '''(^|/)\.venv/''',
27
+ '''(^|/)\.mypy_cache/''',
28
+ '''(^|/)\.ruff_cache/''',
29
+ '''(^|/)\.pytest_cache/''',
30
+ '''(^|/)dist/''',
31
+ # The tier-2 smoke stacks under tests/smoke/ use documented, throwaway
32
+ # default credentials for local-only containers (e.g. MinIO's published
33
+ # "minioadmin"/"minioadmin", Postgres's "continuo"/"continuo") — not
34
+ # secrets, and never reachable outside the compose network they define.
35
+ '''(^|/)tests/smoke/[^/]+/docker-compose\.yml$''',
36
+ ]