holder-kit 0.0.1__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 (63) hide show
  1. holder_kit-0.0.1/.github/actions/core-sdk/action.yml +21 -0
  2. holder_kit-0.0.1/.github/workflows/ci.yml +325 -0
  3. holder_kit-0.0.1/.github/workflows/launchpad.yml +79 -0
  4. holder_kit-0.0.1/.github/workflows/release.yml +243 -0
  5. holder_kit-0.0.1/.github/workflows/ubuntu-packages.yml +47 -0
  6. holder_kit-0.0.1/.gitignore +10 -0
  7. holder_kit-0.0.1/CMakeLists.txt +93 -0
  8. holder_kit-0.0.1/LICENSE +674 -0
  9. holder_kit-0.0.1/PKG-INFO +118 -0
  10. holder_kit-0.0.1/README.md +96 -0
  11. holder_kit-0.0.1/core-source.json +4 -0
  12. holder_kit-0.0.1/docs/building.md +171 -0
  13. holder_kit-0.0.1/docs/development.md +128 -0
  14. holder_kit-0.0.1/docs/packaging.md +184 -0
  15. holder_kit-0.0.1/docs/record-contracts.md +312 -0
  16. holder_kit-0.0.1/docs/walkthrough.md +125 -0
  17. holder_kit-0.0.1/docs/workflow.png +0 -0
  18. holder_kit-0.0.1/examples/card_lifecycle.py +34 -0
  19. holder_kit-0.0.1/examples/detached_records.py +27 -0
  20. holder_kit-0.0.1/examples/graph_analysis.py +27 -0
  21. holder_kit-0.0.1/examples/milestone_analysis.py +31 -0
  22. holder_kit-0.0.1/examples/pandas_analysis.py +44 -0
  23. holder_kit-0.0.1/examples/tag_analysis.py +25 -0
  24. holder_kit-0.0.1/make.sh +26 -0
  25. holder_kit-0.0.1/packaging/linux/debian/changelog +6 -0
  26. holder_kit-0.0.1/packaging/linux/debian/control +25 -0
  27. holder_kit-0.0.1/packaging/linux/debian/copyright +13 -0
  28. holder_kit-0.0.1/packaging/linux/debian/rules +19 -0
  29. holder_kit-0.0.1/packaging/linux/debian/source/format +1 -0
  30. holder_kit-0.0.1/pyproject.toml +32 -0
  31. holder_kit-0.0.1/release-core.json +5 -0
  32. holder_kit-0.0.1/scripts/__init__.py +1 -0
  33. holder_kit-0.0.1/scripts/check-release-distributions.py +57 -0
  34. holder_kit-0.0.1/scripts/configure-holder-ppa.sh +14 -0
  35. holder_kit-0.0.1/scripts/core-sdk.py +17 -0
  36. holder_kit-0.0.1/scripts/develop.py +437 -0
  37. holder_kit-0.0.1/scripts/prepare-debian-source.py +54 -0
  38. holder_kit-0.0.1/scripts/repair-linux-wheel.py +99 -0
  39. holder_kit-0.0.1/scripts/system-core-build-info.py +38 -0
  40. holder_kit-0.0.1/scripts/test-installed-debian.py +42 -0
  41. holder_kit-0.0.1/scripts/test-installed-wheel.py +48 -0
  42. holder_kit-0.0.1/scripts/ubuntu-package-ci.sh +40 -0
  43. holder_kit-0.0.1/scripts/wheel-notices.py +101 -0
  44. holder_kit-0.0.1/src/holderkit/__init__.py +195 -0
  45. holder_kit-0.0.1/src/holderkit/_native.pyi +52 -0
  46. holder_kit-0.0.1/src/holderkit/collections.py +304 -0
  47. holder_kit-0.0.1/src/holderkit/data/__init__.py +36 -0
  48. holder_kit-0.0.1/src/holderkit/data/card.py +166 -0
  49. holder_kit-0.0.1/src/holderkit/data/connection.py +28 -0
  50. holder_kit-0.0.1/src/holderkit/data/milestone.py +71 -0
  51. holder_kit-0.0.1/src/holderkit/data/project.py +96 -0
  52. holder_kit-0.0.1/src/holderkit/data/tag.py +38 -0
  53. holder_kit-0.0.1/src/holderkit/dataframe.py +63 -0
  54. holder_kit-0.0.1/src/holderkit/graph.py +43 -0
  55. holder_kit-0.0.1/src/holderkit/py.typed +0 -0
  56. holder_kit-0.0.1/src/native/module.c +868 -0
  57. holder_kit-0.0.1/tests/test_connections.py +80 -0
  58. holder_kit-0.0.1/tests/test_graph.py +102 -0
  59. holder_kit-0.0.1/tests/test_holder.py +252 -0
  60. holder_kit-0.0.1/tests/test_make.py +264 -0
  61. holder_kit-0.0.1/tests/test_milestones.py +234 -0
  62. holder_kit-0.0.1/tests/test_pandas.py +252 -0
  63. holder_kit-0.0.1/tests/test_tags.py +163 -0
@@ -0,0 +1,21 @@
1
+ name: Fetch core SDK
2
+ description: Use the run-wide immutable core selection and verify cached SDK archives
3
+ inputs:
4
+ commit:
5
+ description: Resolved core commit
6
+ required: true
7
+ build-type:
8
+ description: Core and shim build configuration
9
+ default: RelWithDebInfo
10
+ runs:
11
+ using: composite
12
+ steps:
13
+ - uses: HolderTeam/holder-core/.github/actions/core-sdk@main
14
+ with:
15
+ commit: ${{ inputs.commit }}
16
+ build-type: ${{ inputs.build-type }}
17
+ - name: Configure Python build tools
18
+ shell: bash
19
+ run: |
20
+ echo 'CMAKE_GENERATOR=Ninja' >> "$GITHUB_ENV"
21
+ echo "SKBUILD_CMAKE_BUILD_TYPE=$HOLDER_CORE_BUILD_TYPE" >> "$GITHUB_ENV"
@@ -0,0 +1,325 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - main
8
+ workflow_dispatch:
9
+ inputs:
10
+ core_ref:
11
+ description: Core version tag or full commit SHA (empty selects latest-green)
12
+ required: false
13
+ type: string
14
+ workflow_call:
15
+ inputs:
16
+ core_ref:
17
+ description: Exact published core commit to test
18
+ required: true
19
+ type: string
20
+ kit_ref:
21
+ description: Holder Kit source revision (resolved once for all jobs)
22
+ default: main
23
+ type: string
24
+
25
+ permissions:
26
+ contents: read
27
+
28
+ jobs:
29
+ core-sdk:
30
+ name: Resolve core SDK once
31
+ runs-on: ubuntu-24.04
32
+ outputs:
33
+ commit: ${{ steps.resolve.outputs.commit }}
34
+ kit_commit: ${{ steps.source.outputs.kit_commit }}
35
+ steps:
36
+ - uses: actions/checkout@v7
37
+ with:
38
+ repository: HolderTeam/holder-kit
39
+ ref: ${{ github.repository == 'HolderTeam/holder-kit' && github.sha || inputs.kit_ref || 'main' }}
40
+ persist-credentials: false
41
+ - id: source
42
+ name: Record Holder Kit source revision
43
+ shell: bash
44
+ run: echo "kit_commit=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
45
+ - uses: actions/setup-python@v7
46
+ with:
47
+ python-version: "3.12"
48
+ - id: resolve
49
+ uses: HolderTeam/holder-core/.github/actions/core-sdk@main
50
+ with:
51
+ command: resolve
52
+ core-ref: ${{ inputs.core_ref || 'latest-green' }}
53
+
54
+ linux:
55
+ needs: core-sdk
56
+ name: ${{ matrix.name }}
57
+ runs-on: ${{ matrix.runner }}
58
+ timeout-minutes: 45
59
+ strategy:
60
+ fail-fast: false
61
+ matrix:
62
+ include:
63
+ - runner: ubuntu-24.04
64
+ name: Ubuntu 24.04 / Python 3.12 / compatibility
65
+ python-version: "3.12"
66
+ comprehensive: true
67
+ sanitized: false
68
+ # Use the canonical SDK's distribution dependency ABI. Native
69
+ # Ubuntu-series integration belongs to the Debian package builds.
70
+ - runner: ubuntu-24.04
71
+ name: Ubuntu 24.04 / Python 3.14 / shim sanitizers
72
+ python-version: "3.14"
73
+ comprehensive: false
74
+ sanitized: true
75
+
76
+ steps:
77
+ - name: Check out Holder Kit
78
+ uses: actions/checkout@v7
79
+ with:
80
+ repository: HolderTeam/holder-kit
81
+ ref: ${{ needs.core-sdk.outputs.kit_commit }}
82
+ persist-credentials: false
83
+
84
+ - name: Fetch verified core SDK
85
+ uses: ./.github/actions/core-sdk
86
+ with:
87
+ commit: ${{ needs.core-sdk.outputs.commit }}
88
+
89
+ - name: Install native build dependencies
90
+ run: |
91
+ sudo apt-get update
92
+ sudo apt-get install -y --no-install-recommends \
93
+ build-essential \
94
+ cmake \
95
+ ninja-build \
96
+ pkg-config \
97
+ python3-dev \
98
+ python3-venv \
99
+ libssl-dev \
100
+ nlohmann-json3-dev \
101
+ libyaml-cpp-dev \
102
+ libspdlog-dev \
103
+ libsqlite3-dev \
104
+ libsodium-dev \
105
+ libgit2-dev \
106
+ libmd4c-dev \
107
+ libsecret-1-dev
108
+
109
+ - name: Set up selected Python
110
+ uses: actions/setup-python@v7
111
+ with:
112
+ python-version: ${{ matrix.python-version }}
113
+
114
+ - name: Create isolated CI environment
115
+ run: |
116
+ python3 -m venv "${RUNNER_TEMP}/holder-ci-venv"
117
+ echo "${RUNNER_TEMP}/holder-ci-venv/bin" >> "${GITHUB_PATH}"
118
+
119
+ - name: Install packaging tools
120
+ run: python3 -m pip install --upgrade pip
121
+
122
+ - name: Build and install Holder Kit with complete test dependencies
123
+ if: ${{ matrix.comprehensive }}
124
+ run: python3 -m pip install --verbose '.[test]'
125
+
126
+ - name: Build instrumented Holder Kit for lifecycle smoke tests
127
+ if: ${{ matrix.sanitized }}
128
+ run: python3 -m pip install --verbose --config-settings=cmake.define.HOLDER_KIT_SANITIZE=address,undefined . 'pytest>=8'
129
+
130
+ - name: Configure sanitizer runtime
131
+ if: ${{ matrix.sanitized }}
132
+ run: |
133
+ asan_library="$(gcc -print-file-name=libasan.so)"
134
+ if ! file -L "${asan_library}" | grep -q 'ELF'; then
135
+ asan_library="$(ldconfig -p | awk '/libasan\.so/{print $NF; exit}')"
136
+ fi
137
+ test -f "${asan_library}"
138
+ echo "LD_PRELOAD=${asan_library}" >> "${GITHUB_ENV}"
139
+ echo "ASAN_OPTIONS=detect_leaks=0:halt_on_error=1" >> "${GITHUB_ENV}"
140
+ echo "UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1" >> "${GITHUB_ENV}"
141
+
142
+ - name: Show installed build
143
+ run: |
144
+ python3 --version
145
+ python3 -c 'import sys; print(sys.executable)'
146
+ python3 -m pip show holder-kit
147
+ python3 -c 'import holderkit; print(holderkit.__file__)'
148
+
149
+ - name: Run instrumented Python lifecycle smoke tests
150
+ if: ${{ matrix.sanitized }}
151
+ run: |
152
+ python3 -m pytest --verbose \
153
+ tests/test_holder.py::test_native_extension_imports \
154
+ tests/test_holder.py::test_complete_card_lifecycle \
155
+ tests/test_holder.py::test_context_destruction_releases_native_resources
156
+
157
+ - name: Run complete Python tests
158
+ if: ${{ matrix.comprehensive }}
159
+ run: python3 -m pytest --verbose
160
+
161
+ - name: Run strict type checking
162
+ if: ${{ matrix.comprehensive }}
163
+ run: python3 -m mypy
164
+
165
+ - name: Run lifecycle example
166
+ run: python3 examples/card_lifecycle.py
167
+
168
+ - name: Run detached-record and pandas examples
169
+ if: ${{ matrix.comprehensive }}
170
+ run: |
171
+ python3 examples/detached_records.py
172
+ python3 examples/pandas_analysis.py
173
+
174
+ - name: Build wheel
175
+ if: ${{ matrix.comprehensive }}
176
+ run: |
177
+ python3 -m pip install build
178
+ python3 -m build --wheel --outdir dist
179
+ ls -lh dist
180
+
181
+ - name: Install wheel in a fresh base environment
182
+ if: ${{ matrix.comprehensive }}
183
+ run: |
184
+ python3 -m venv "${RUNNER_TEMP}/holder-wheel-venv"
185
+ "${RUNNER_TEMP}/holder-wheel-venv/bin/python" -m pip install --upgrade pip
186
+ "${RUNNER_TEMP}/holder-wheel-venv/bin/python" -m pip install --verbose dist/*.whl
187
+
188
+ - name: Smoke-test installed wheel without pandas
189
+ if: ${{ matrix.comprehensive }}
190
+ run: |
191
+ "${RUNNER_TEMP}/holder-wheel-venv/bin/python" - <<'PY'
192
+ import importlib.util
193
+ import json
194
+ from importlib.metadata import distribution
195
+ from importlib.resources import files
196
+ import sys
197
+ import tempfile
198
+ from pathlib import Path
199
+
200
+ assert importlib.util.find_spec("pandas") is None
201
+
202
+ import holderkit
203
+ from holderkit import _native
204
+
205
+ installed = distribution("holder-kit")
206
+ assert installed.metadata["Name"] == "holder-kit"
207
+ assert installed.files is not None
208
+ assert all(path.parts[0] != "holder" for path in installed.files)
209
+ assert importlib.util.find_spec("holder") is None
210
+ assert _native.__name__ == "holderkit._native"
211
+ assert _native.Context.__module__ == "holderkit._native"
212
+ assert holderkit.HolderError.__module__ == "holderkit._native"
213
+ resources = files("holderkit")
214
+ assert resources.joinpath("py.typed").is_file()
215
+ assert resources.joinpath("_schema.sql").read_text(encoding="utf-8")
216
+ assert json.loads(resources.joinpath("_core_build.json").read_text(encoding="utf-8"))
217
+
218
+ assert "pandas" not in sys.modules
219
+ with tempfile.TemporaryDirectory(prefix="holder-wheel-smoke-") as temporary:
220
+ with holderkit.open(Path(temporary) / "data") as context:
221
+ project = context.create_project("Wheel smoke test")
222
+ card = context.create_card(
223
+ project.project_id, "Installed wheel", "standalone body"
224
+ )
225
+ assert context.get_card_content(card.card_id) == "standalone body"
226
+ assert context.cards.to_records(project.project_id)[0]["card_id"] == card.card_id
227
+ try:
228
+ context.cards.to_dataframe(project.project_id)
229
+ except ModuleNotFoundError as error:
230
+ assert "holder-kit[pandas]" in str(error)
231
+ else:
232
+ raise AssertionError("base wheel unexpectedly provided pandas")
233
+ PY
234
+
235
+ macos:
236
+ needs: core-sdk
237
+ name: macOS 15 / Python 3.12
238
+ runs-on: macos-15
239
+ timeout-minutes: 30
240
+
241
+ steps:
242
+ - name: Check out Holder Kit
243
+ uses: actions/checkout@v7
244
+ with:
245
+ repository: HolderTeam/holder-kit
246
+ ref: ${{ needs.core-sdk.outputs.kit_commit }}
247
+ persist-credentials: false
248
+
249
+ - name: Set up Python 3.12
250
+ uses: actions/setup-python@v7
251
+ with:
252
+ python-version: "3.12"
253
+
254
+ - name: Fetch verified core SDK
255
+ uses: ./.github/actions/core-sdk
256
+ with:
257
+ commit: ${{ needs.core-sdk.outputs.commit }}
258
+
259
+ - name: Install native build dependencies
260
+ run: |
261
+ brew install \
262
+ cmake \
263
+ ninja \
264
+ pkg-config \
265
+ nlohmann-json \
266
+ yaml-cpp \
267
+ spdlog \
268
+ sqlite \
269
+ libsodium \
270
+ libgit2 \
271
+ md4c \
272
+ openssl@3
273
+
274
+ - name: Build and install Holder Kit
275
+ run: |
276
+ python3 -m pip install --upgrade pip
277
+ CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_PREFIX_PATH=$(brew --prefix);$(brew --prefix openssl@3) -DOPENSSL_ROOT_DIR=$(brew --prefix openssl@3)" \
278
+ python3 -m pip install --verbose . 'pytest>=8'
279
+
280
+ - name: Run base Python tests
281
+ run: python3 -m pytest --verbose tests/test_holder.py
282
+
283
+ - name: Run lifecycle example
284
+ run: python3 examples/card_lifecycle.py
285
+
286
+ windows:
287
+ needs: core-sdk
288
+ name: Windows Server 2025 / Python 3.12
289
+ runs-on: windows-2025
290
+ timeout-minutes: 45
291
+
292
+ steps:
293
+ - name: Check out Holder Kit
294
+ uses: actions/checkout@v7
295
+ with:
296
+ repository: HolderTeam/holder-kit
297
+ ref: ${{ needs.core-sdk.outputs.kit_commit }}
298
+ persist-credentials: false
299
+
300
+ - name: Set up Python 3.12
301
+ uses: actions/setup-python@v7
302
+ with:
303
+ python-version: "3.12"
304
+
305
+ - name: Set up MSVC developer environment
306
+ uses: ilammy/msvc-dev-cmd@v1
307
+
308
+ - name: Fetch verified core SDK
309
+ uses: ./.github/actions/core-sdk
310
+ with:
311
+ commit: ${{ needs.core-sdk.outputs.commit }}
312
+
313
+ - name: Build and install Holder Kit
314
+ run: |
315
+ python -m pip install --upgrade pip
316
+ python -m pip install --verbose . "pytest>=8"
317
+
318
+ - name: Verify native extension and bundled runtime dependencies
319
+ run: python -c "import holderkit; print(holderkit.__file__)"
320
+
321
+ - name: Run base Python tests
322
+ run: python -m pytest --verbose tests/test_holder.py
323
+
324
+ - name: Run lifecycle example
325
+ run: python examples/card_lifecycle.py
@@ -0,0 +1,79 @@
1
+ name: Prepare Holder Kit Launchpad packages
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ version:
7
+ description: Expected pyproject.toml version
8
+ required: true
9
+ default: '0.0.1'
10
+ package_revision:
11
+ description: Debian revision (increment for each replacement upload)
12
+ required: true
13
+ default: '1'
14
+ core_package_version:
15
+ description: Exact core package version before the Ubuntu suffix
16
+ required: true
17
+ default: '0.2.0-1'
18
+ upload:
19
+ description: Sign and upload the source candidates to the Holder PPA
20
+ type: boolean
21
+ default: false
22
+
23
+ permissions:
24
+ contents: read
25
+
26
+ jobs:
27
+ source:
28
+ runs-on: ubuntu-24.04
29
+ strategy:
30
+ fail-fast: false
31
+ matrix:
32
+ series: [noble, resolute]
33
+ steps:
34
+ - uses: actions/checkout@v7
35
+ with:
36
+ persist-credentials: false
37
+ - name: Install source packaging tools
38
+ run: |
39
+ sudo apt-get update
40
+ sudo apt-get install -y --no-install-recommends devscripts debhelper dh-python dput gnupg
41
+ - name: Prepare pinned source candidate
42
+ env:
43
+ UPSTREAM_VERSION: ${{ inputs.version }}
44
+ PACKAGE_REVISION: ${{ inputs.package_revision }}
45
+ CORE_PACKAGE_VERSION: ${{ inputs.core_package_version }}
46
+ SERIES: ${{ matrix.series }}
47
+ run: |
48
+ python3 scripts/prepare-debian-source.py --version "$UPSTREAM_VERSION" \
49
+ --package-revision "$PACKAGE_REVISION" --series "$SERIES" \
50
+ --core-package-version "${CORE_PACKAGE_VERSION}~${SERIES}1"
51
+ debuild -S -d -us -uc
52
+ - name: Save source candidate
53
+ run: |
54
+ mkdir -p out/source
55
+ cp ../holder-kit_*.dsc ../holder-kit_*.tar.* \
56
+ ../holder-kit_*_source.changes ../holder-kit_*_source.buildinfo out/source/
57
+ - uses: actions/upload-artifact@v7
58
+ with:
59
+ name: holder-kit-source-${{ matrix.series }}
60
+ path: out/source/*
61
+ if-no-files-found: error
62
+ - name: Sign source candidate
63
+ if: inputs.upload
64
+ env:
65
+ PPA_GPG_PRIVATE_KEY: ${{ secrets.PPA_GPG_PRIVATE_KEY }}
66
+ run: |
67
+ test -n "$PPA_GPG_PRIVATE_KEY"
68
+ mkdir -p ~/.gnupg
69
+ chmod 700 ~/.gnupg
70
+ printf '%s\n' "$PPA_GPG_PRIVATE_KEY" | gpg --batch --import
71
+ keyid="$(gpg --list-secret-keys --with-colons | awk -F: '/^sec:/ {print $5; exit}')"
72
+ test -n "$keyid"
73
+ printf 'batch\nyes\npinentry-mode loopback\n' > ~/.gnupg/gpg.conf
74
+ printf 'allow-loopback-pinentry\n' > ~/.gnupg/gpg-agent.conf
75
+ gpgconf --kill gpg-agent
76
+ debsign -k"$keyid" ../holder-kit_*_source.changes
77
+ - name: Upload source candidate
78
+ if: inputs.upload
79
+ run: dput ppa:holderteam/holder ../holder-kit_*_source.changes
@@ -0,0 +1,243 @@
1
+ name: Kit release wheels
2
+
3
+ on:
4
+ pull_request:
5
+ paths:
6
+ - .github/workflows/release.yml
7
+ - .github/actions/core-sdk/**
8
+ - release-core.json
9
+ - pyproject.toml
10
+ - CMakeLists.txt
11
+ - src/**
12
+ - scripts/*wheel*.py
13
+ - scripts/check-release-distributions.py
14
+ workflow_dispatch:
15
+ inputs:
16
+ destination:
17
+ description: Build candidates, or publish a version tag after validation
18
+ type: choice
19
+ options: [none, testpypi, pypi]
20
+ default: none
21
+
22
+ permissions:
23
+ contents: read
24
+
25
+ jobs:
26
+ prepare:
27
+ runs-on: ubuntu-24.04
28
+ outputs:
29
+ version: ${{ steps.config.outputs.version }}
30
+ core_commit: ${{ steps.config.outputs.core_commit }}
31
+ steps:
32
+ - uses: actions/checkout@v7
33
+ with:
34
+ persist-credentials: false
35
+ - uses: actions/setup-python@v7
36
+ with:
37
+ python-version: '3.12'
38
+ - id: config
39
+ env:
40
+ DESTINATION: ${{ inputs.destination || 'none' }}
41
+ run: |
42
+ python - <<'PY'
43
+ import json, os, re, tomllib
44
+ from pathlib import Path
45
+ version = tomllib.loads(Path('pyproject.toml').read_text())['project']['version']
46
+ pin = json.loads(Path('release-core.json').read_text())
47
+ assert pin['repository'] == 'HolderTeam/holder-core'
48
+ assert re.fullmatch(r'[0-9a-f]{40}', pin['commit'])
49
+ assert pin['build_type'] == 'Release'
50
+ if os.environ['DESTINATION'] != 'none':
51
+ assert os.environ['GITHUB_REF'] == f'refs/tags/v{version}', 'Publishing requires the matching version tag'
52
+ with open(os.environ['GITHUB_OUTPUT'], 'a') as output:
53
+ output.write(f'version={version}\ncore_commit={pin["commit"]}\n')
54
+ PY
55
+ - uses: HolderTeam/holder-core/.github/actions/core-sdk@main
56
+ with:
57
+ command: resolve
58
+ core-ref: ${{ steps.config.outputs.core_commit }}
59
+
60
+ wheels:
61
+ needs: prepare
62
+ runs-on: ${{ matrix.runner }}
63
+ timeout-minutes: 45
64
+ strategy:
65
+ fail-fast: false
66
+ matrix:
67
+ python: ['3.10', '3.11', '3.12', '3.13', '3.14']
68
+ runner: [ubuntu-24.04, windows-2025, macos-15]
69
+ steps:
70
+ - uses: actions/checkout@v7
71
+ with:
72
+ persist-credentials: false
73
+ - uses: actions/setup-python@v7
74
+ with:
75
+ python-version: ${{ matrix.python }}
76
+ - uses: ilammy/msvc-dev-cmd@v1
77
+ if: runner.os == 'Windows'
78
+ - uses: ./.github/actions/core-sdk
79
+ with:
80
+ commit: ${{ needs.prepare.outputs.core_commit }}
81
+ build-type: Release
82
+ - name: Linux native dependencies
83
+ if: runner.os == 'Linux'
84
+ run: |
85
+ sudo apt-get update
86
+ sudo apt-get install -y --no-install-recommends build-essential cmake ninja-build pkg-config \
87
+ patchelf libssl-dev nlohmann-json3-dev libyaml-cpp-dev libspdlog-dev \
88
+ libsqlite3-dev libsodium-dev libgit2-dev libmd4c-dev libsecret-1-dev
89
+ - name: macOS native dependencies
90
+ if: runner.os == 'macOS'
91
+ run: |
92
+ brew install cmake ninja pkg-config nlohmann-json yaml-cpp spdlog sqlite \
93
+ libsodium libgit2 md4c openssl@3
94
+ echo 'MACOSX_DEPLOYMENT_TARGET=15.0' >> "$GITHUB_ENV"
95
+ echo "CMAKE_ARGS=$CMAKE_ARGS -DCMAKE_PREFIX_PATH=$(brew --prefix);$(brew --prefix openssl@3) -DOPENSSL_ROOT_DIR=$(brew --prefix openssl@3) -DCMAKE_OSX_ARCHITECTURES=arm64" >> "$GITHUB_ENV"
96
+ - name: Build wheel
97
+ shell: bash
98
+ run: |
99
+ python -m pip install --upgrade pip
100
+ python -m pip install build wheel twine
101
+ python -m build --wheel --outdir out/raw
102
+ - name: Bundle Linux runtime and validate manylinux ABI
103
+ if: runner.os == 'Linux'
104
+ run: |
105
+ python -m pip install 'auditwheel==6.8.2'
106
+ python scripts/repair-linux-wheel.py --output dist out/raw/*.whl
107
+ - name: Bundle macOS runtime
108
+ if: runner.os == 'macOS'
109
+ run: |
110
+ python -m pip install delocate
111
+ python scripts/wheel-notices.py --output out/noticed out/raw/*.whl
112
+ delocate-wheel --require-archs arm64 -v -w dist out/noticed/*.whl
113
+ - name: Bundle Windows runtime
114
+ if: runner.os == 'Windows'
115
+ shell: bash
116
+ run: |
117
+ python -m pip install delvewheel
118
+ python scripts/wheel-notices.py --output out/noticed out/raw/*.whl
119
+ python -m delvewheel repair --add-path "$HOLDER_CORE_SDK/bin" -w dist out/noticed/*.whl
120
+ - name: Check distribution metadata
121
+ shell: bash
122
+ run: python -m twine check --strict dist/*.whl
123
+ - name: Install wheel in a new virtualenv
124
+ shell: bash
125
+ env:
126
+ CORE_COMMIT: ${{ needs.prepare.outputs.core_commit }}
127
+ KIT_VERSION: ${{ needs.prepare.outputs.version }}
128
+ run: |
129
+ python -m venv out/test-venv
130
+ if [ "$RUNNER_OS" = Windows ]; then
131
+ test_python=out/test-venv/Scripts/python.exe
132
+ else
133
+ test_python=out/test-venv/bin/python
134
+ fi
135
+ "$test_python" -m pip install --no-deps dist/*.whl
136
+ env -u HOLDER_CORE_SDK -u CMAKE_ARGS -u LD_LIBRARY_PATH -u DYLD_LIBRARY_PATH \
137
+ "$test_python" -I scripts/test-installed-wheel.py --core-commit "$CORE_COMMIT" --version "$KIT_VERSION"
138
+ "$test_python" -m pip install 'pytest>=8' pandas networkx
139
+ "$test_python" -I -m pytest -q --import-mode=importlib tests/test_holder.py \
140
+ tests/test_connections.py tests/test_graph.py tests/test_pandas.py
141
+ - name: Linux wheel in a clean Debian runtime
142
+ if: runner.os == 'Linux'
143
+ env:
144
+ PYTHON_VERSION: ${{ matrix.python }}
145
+ CORE_COMMIT: ${{ needs.prepare.outputs.core_commit }}
146
+ KIT_VERSION: ${{ needs.prepare.outputs.version }}
147
+ run: |
148
+ docker run --rm -v "$PWD:/source:ro" \
149
+ -e CORE_COMMIT -e KIT_VERSION "python:${PYTHON_VERSION}-slim-trixie" \
150
+ bash -c 'python -m venv /venv && /venv/bin/python -m pip install --no-deps /source/dist/*.whl && cd /tmp && /venv/bin/python -I /source/scripts/test-installed-wheel.py --core-commit "$CORE_COMMIT" --version "$KIT_VERSION"'
151
+ - uses: actions/upload-artifact@v7
152
+ with:
153
+ name: wheel-${{ matrix.runner }}-${{ matrix.python }}
154
+ path: dist/*.whl
155
+ if-no-files-found: error
156
+
157
+ sdist:
158
+ needs: prepare
159
+ runs-on: ubuntu-24.04
160
+ steps:
161
+ - uses: actions/checkout@v7
162
+ with:
163
+ persist-credentials: false
164
+ - uses: actions/setup-python@v7
165
+ with:
166
+ python-version: '3.12'
167
+ - run: |
168
+ python -m pip install build twine
169
+ python -m build --sdist --outdir dist
170
+ python -m twine check --strict dist/*.tar.gz
171
+ - uses: ./.github/actions/core-sdk
172
+ with:
173
+ commit: ${{ needs.prepare.outputs.core_commit }}
174
+ build-type: Release
175
+ - name: Build and install the source archive with the pinned SDK
176
+ env:
177
+ CORE_COMMIT: ${{ needs.prepare.outputs.core_commit }}
178
+ KIT_VERSION: ${{ needs.prepare.outputs.version }}
179
+ run: |
180
+ sudo apt-get update
181
+ sudo apt-get install -y --no-install-recommends build-essential cmake ninja-build pkg-config \
182
+ libssl-dev nlohmann-json3-dev libyaml-cpp-dev libspdlog-dev libsqlite3-dev \
183
+ libsodium-dev libgit2-dev libmd4c-dev libsecret-1-dev
184
+ python -m venv out/source-test
185
+ out/source-test/bin/python -m pip install --no-deps dist/*.tar.gz
186
+ out/source-test/bin/python -I scripts/test-installed-wheel.py --core-commit "$CORE_COMMIT" --version "$KIT_VERSION"
187
+ - uses: actions/upload-artifact@v7
188
+ with:
189
+ name: source-distribution
190
+ path: dist/*.tar.gz
191
+ if-no-files-found: error
192
+
193
+ distributions:
194
+ needs: [prepare, wheels, sdist]
195
+ runs-on: ubuntu-24.04
196
+ steps:
197
+ - uses: actions/checkout@v7
198
+ with:
199
+ persist-credentials: false
200
+ - uses: actions/setup-python@v7
201
+ with:
202
+ python-version: '3.12'
203
+ - uses: actions/download-artifact@v4
204
+ with:
205
+ pattern: wheel-*
206
+ path: dist
207
+ merge-multiple: true
208
+ - uses: actions/download-artifact@v4
209
+ with:
210
+ name: source-distribution
211
+ path: dist
212
+ - run: python scripts/check-release-distributions.py dist --manifest out/release-manifest.json
213
+ - uses: actions/upload-artifact@v7
214
+ with:
215
+ name: release-distributions
216
+ path: dist/*
217
+ if-no-files-found: error
218
+ - uses: actions/upload-artifact@v7
219
+ with:
220
+ name: release-manifest
221
+ path: out/release-manifest.json
222
+
223
+ publish:
224
+ if: github.event_name == 'workflow_dispatch' && inputs.destination != 'none'
225
+ needs: distributions
226
+ runs-on: ubuntu-24.04
227
+ environment: ${{ inputs.destination }}
228
+ permissions:
229
+ contents: read
230
+ id-token: write
231
+ steps:
232
+ - uses: actions/download-artifact@v4
233
+ with:
234
+ name: release-distributions
235
+ path: dist
236
+ - name: Publish to TestPyPI
237
+ if: inputs.destination == 'testpypi'
238
+ uses: pypa/gh-action-pypi-publish@release/v1
239
+ with:
240
+ repository-url: https://test.pypi.org/legacy/
241
+ - name: Publish to PyPI
242
+ if: inputs.destination == 'pypi'
243
+ uses: pypa/gh-action-pypi-publish@release/v1