Skip to content

RFC 0035: a Python-free type layer #1271

RFC 0035: a Python-free type layer

RFC 0035: a Python-free type layer #1271

name: Build, test, and publish Python distributions
on:
push:
branches:
- main
tags:
- "*.*.*"
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
actions: read
contents: read
env:
GCC_VERSION: "14"
SCCACHE_GHA_ENABLED: "true"
jobs:
validate-release:
name: Validate release metadata
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.12"
- name: Validate package metadata
run: |
python -m pip install --upgrade --only-binary=:all: \
"packaging==26.3" "trove-classifiers==2026.6.1.19"
python python/tests/test_packaging.py
- name: Validate release tag
if: github.ref_type == 'tag'
env:
RELEASE_TAG: ${{ github.ref_name }}
run: python tools/validate_release.py "$RELEASE_TAG"
reuse-build:
name: Find tested distributions for this commit
needs: validate-release
runs-on: ubuntu-latest
outputs:
run-id: ${{ steps.find.outputs.run-id }}
steps:
- name: Find successful build by commit SHA
id: find
if: github.ref_type == 'tag'
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
script: |
const requiredArtifacts = new Set([
"distribution-sdist",
"distribution-wheel-macos-26",
"distribution-wheel-ubuntu-latest",
"distribution-wheel-windows-latest",
"kafka-distribution-sdist",
"kafka-distribution-wheel-macos-26",
"kafka-distribution-wheel-ubuntu-latest",
"kafka-distribution-wheel-windows-latest",
"analytics-distribution-sdist",
"analytics-distribution-wheel-macos-26",
"analytics-distribution-wheel-ubuntu-latest",
"analytics-distribution-wheel-windows-latest",
"persistence-distribution-sdist",
"persistence-distribution-wheel-macos-26",
"persistence-distribution-wheel-ubuntu-latest",
"persistence-distribution-wheel-windows-latest",
"fabric-distribution-sdist",
"fabric-distribution-wheel-macos-26",
"fabric-distribution-wheel-ubuntu-latest",
"fabric-distribution-wheel-windows-latest",
"web-distribution-sdist",
"web-distribution-wheel-macos-26",
"web-distribution-wheel-ubuntu-latest",
"web-distribution-wheel-windows-latest",
]);
const runs = await github.paginate(
github.rest.actions.listWorkflowRuns,
{
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: "release-wheels.yml",
event: "push",
head_sha: context.sha,
status: "success",
per_page: 100,
},
);
for (const run of runs) {
if (run.head_sha !== context.sha || run.id === context.runId) {
continue;
}
const artifacts = await github.paginate(
github.rest.actions.listWorkflowRunArtifacts,
{
owner: context.repo.owner,
repo: context.repo.repo,
run_id: run.id,
per_page: 100,
},
);
const availableArtifacts = new Set(
artifacts
.filter((artifact) => !artifact.expired)
.map((artifact) => artifact.name),
);
if ([...requiredArtifacts].every((name) => availableArtifacts.has(name))) {
core.notice(`Reusing tested distributions from workflow run ${run.id}`);
core.setOutput("run-id", String(run.id));
return;
}
}
core.notice(`No reusable distributions found for commit ${context.sha}`);
core.setOutput("run-id", "");
build-windows-wheel:
name: Build Windows wheel artifacts
needs:
- validate-release
- reuse-build
if: needs.reuse-build.outputs.run-id == ''
uses: ./.github/workflows/release-platform-wheel.yml
with:
os: windows-latest
parallel: "4"
# Ninja (not the Visual Studio generator): MSBuild ignores
# CMAKE_CXX_COMPILER_LAUNCHER, so sccache never fires under the VS
# generator. /Z7 keeps debug-carrying configurations cacheable because
# sccache cannot cache /Zi's shared PDB.
cmake_args: >-
-DHGRAPH_WARNINGS_AS_ERRORS=OFF
-DCMAKE_POLICY_DEFAULT_CMP0141=NEW
"-DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=$<$<CONFIG:Debug,RelWithDebInfo>:Embedded>"
build-macos-wheel:
name: Build macOS wheel artifacts
needs:
- validate-release
- reuse-build
if: needs.reuse-build.outputs.run-id == ''
uses: ./.github/workflows/release-platform-wheel.yml
with:
os: macos-26
parallel: "1"
cmake_args: -DHGRAPH_WARNINGS_AS_ERRORS=ON -DCMAKE_OSX_DEPLOYMENT_TARGET=15.0
build-linux-wheel:
name: Build manylinux 2.28 / GCC 14 wheel
needs:
- validate-release
- reuse-build
if: needs.reuse-build.outputs.run-id == ''
runs-on: ubuntu-24.04
container:
image: quay.io/pypa/manylinux_2_28_x86_64:latest
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- name: Enable shared compiler cache
uses: mozilla-actions/sccache-action@9e7fa8a12102821edf02ca5dbea1acd0f89a2696 # v0.0.10
with:
version: v0.15.0
- name: Verify official build toolchain
shell: bash
run: |
g++ --version
test "$(g++ -dumpfullversion -dumpversion | cut -d. -f1)" -eq "$GCC_VERSION"
echo "CC=$(command -v gcc)" >> "$GITHUB_ENV"
echo "CXX=$(command -v g++)" >> "$GITHUB_ENV"
/opt/python/cp312-cp312/bin/python --version
- name: Install wheel build tools
run: >-
/opt/python/cp312-cp312/bin/python -m pip install --upgrade
--only-binary=:all: "build==1.5.0" "abi3audit==0.0.26"
"auditwheel==6.7.0"
- name: Build stable ABI wheel
env:
CMAKE_GENERATOR: Ninja
CMAKE_ARGS: -DHGRAPH_WARNINGS_AS_ERRORS=ON
CMAKE_BUILD_PARALLEL_LEVEL: "2"
run: /opt/python/cp312-cp312/bin/python -m build --wheel
- name: Audit wheel contents
run: /opt/python/cp312-cp312/bin/python tools/audit_distribution.py "dist/*.whl"
- name: Repair for the glibc 2.28 floor
run: |
mkdir wheelhouse
auditwheel repair \
--plat manylinux_2_28_x86_64 \
--exclude libarrow.so.2500 \
--exclude libarrow_compute.so.2500 \
--exclude libarrow_acero.so.2500 \
--exclude libhgraph_runtime.so \
--exclude libhgraph_wiring.so \
--exclude libhgraph_stdlib.so \
--exclude libnanobind-abi3.so \
--wheel-dir wheelhouse \
dist/*.whl
rm dist/*.whl
mv wheelhouse/*.whl dist/
- name: Verify wheel ABI and platform tag
run: |
/opt/python/cp312-cp312/bin/python - <<'PY'
from pathlib import Path
import subprocess
from packaging.tags import Tag
from packaging.utils import parse_wheel_filename
wheels = list(Path("dist").glob("*.whl"))
assert len(wheels) == 1, wheels
wheel = wheels[0]
tags = parse_wheel_filename(wheel.name)[3]
expected = Tag("cp312", "abi3", "manylinux_2_28_x86_64")
assert expected in tags, (wheel.name, tags)
subprocess.check_call(["abi3audit", "--strict", str(wheel)])
PY
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: distribution-wheel-ubuntu-latest
path: dist/*.whl
if-no-files-found: error
- name: Install the matching core SDK
run: >-
/opt/python/cp312-cp312/bin/python -m pip install
--only-binary=:all: dist/*.whl
- name: Install Kafka wheel build tools
run: >-
/opt/python/cp312-cp312/bin/python -m pip install --upgrade
--only-binary=:all: "scikit-build-core==1.0.3"
"nanobind==2.13.0" "ninja==1.13.0" "pyarrow==25.0.0"
- name: Build Kafka stable ABI wheel
shell: bash
run: |
hgraph_sdk="$(/opt/python/cp312-cp312/bin/python -c 'import site; print(site.getsitepackages()[0])')"
CMAKE_GENERATOR=Ninja \
CMAKE_ARGS=-DHGRAPH_WARNINGS_AS_ERRORS=ON \
CMAKE_BUILD_PARALLEL_LEVEL=2 \
CMAKE_PREFIX_PATH="$hgraph_sdk" \
/opt/python/cp312-cp312/bin/python -m build \
--wheel --no-isolation --skip-dependency-check \
--outdir kafka-dist-raw extensions/kafka
- name: Repair Kafka wheel for the glibc 2.28 floor
run: |
mkdir kafka-dist
auditwheel repair \
--plat manylinux_2_28_x86_64 \
--exclude libarrow.so.2500 \
--exclude libarrow_compute.so.2500 \
--exclude libarrow_acero.so.2500 \
--exclude libhgraph_runtime.so \
--exclude libhgraph_wiring.so \
--exclude libhgraph_stdlib.so \
--exclude libnanobind-abi3.so \
--wheel-dir kafka-dist \
kafka-dist-raw/*.whl
- name: Verify Kafka wheel ABI and platform tag
run: |
/opt/python/cp312-cp312/bin/python - <<'PY'
from pathlib import Path
import subprocess
from packaging.tags import Tag
from packaging.utils import parse_wheel_filename
wheels = list(Path("kafka-dist").glob("*.whl"))
assert len(wheels) == 1, wheels
wheel = wheels[0]
assert wheel.name.startswith("hgraph_kafka-"), wheel.name
tags = parse_wheel_filename(wheel.name)[3]
expected = Tag("cp312", "abi3", "manylinux_2_28_x86_64")
assert expected in tags, (wheel.name, tags)
subprocess.check_call(["abi3audit", "--strict", str(wheel)])
PY
- name: Audit Kafka wheel contents
run: /opt/python/cp312-cp312/bin/python extensions/kafka/tools/audit_distribution.py "kafka-dist/*.whl"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: kafka-distribution-wheel-ubuntu-latest
path: kafka-dist/*.whl
if-no-files-found: error
- name: Build analytics stable ABI wheel
shell: bash
run: |
hgraph_sdk="$(/opt/python/cp312-cp312/bin/python -c 'import site; print(site.getsitepackages()[0])')"
CMAKE_GENERATOR=Ninja \
CMAKE_ARGS=-DHGRAPH_WARNINGS_AS_ERRORS=ON \
CMAKE_BUILD_PARALLEL_LEVEL=2 \
CMAKE_PREFIX_PATH="$hgraph_sdk" \
/opt/python/cp312-cp312/bin/python -m build \
--wheel --no-isolation --skip-dependency-check \
--outdir analytics-dist-raw extensions/analytics
- name: Repair analytics wheel for the glibc 2.28 floor
run: |
mkdir analytics-dist
auditwheel repair \
--plat manylinux_2_28_x86_64 \
--exclude libarrow.so.2500 \
--exclude libarrow_compute.so.2500 \
--exclude libarrow_acero.so.2500 \
--exclude libhgraph_runtime.so \
--exclude libhgraph_wiring.so \
--exclude libhgraph_stdlib.so \
--exclude libnanobind-abi3.so \
--wheel-dir analytics-dist \
analytics-dist-raw/*.whl
- name: Verify analytics wheel ABI and platform tag
run: |
/opt/python/cp312-cp312/bin/python - <<'PY'
from pathlib import Path
import subprocess
from packaging.tags import Tag
from packaging.utils import parse_wheel_filename
wheels = list(Path("analytics-dist").glob("*.whl"))
assert len(wheels) == 1, wheels
wheel = wheels[0]
assert wheel.name.startswith("hgraph_analytics-"), wheel.name
tags = parse_wheel_filename(wheel.name)[3]
expected = Tag("cp312", "abi3", "manylinux_2_28_x86_64")
assert expected in tags, (wheel.name, tags)
subprocess.check_call(["abi3audit", "--strict", str(wheel)])
PY
- name: Audit analytics wheel contents
run: >-
/opt/python/cp312-cp312/bin/python
extensions/analytics/tools/audit_distribution.py
"analytics-dist/*.whl"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: analytics-distribution-wheel-ubuntu-latest
path: analytics-dist/*.whl
if-no-files-found: error
- name: Install persistence wheel TLS dependency
# The shared persistence SDK carries the pinned curl used for S3
# conditional writes; auditwheel vendors its TLS runtime libraries.
shell: bash
run: dnf install -y openssl-devel
- name: Build persistence stable ABI wheel
shell: bash
run: |
hgraph_sdk="$(/opt/python/cp312-cp312/bin/python -c 'import site; print(site.getsitepackages()[0])')"
CMAKE_GENERATOR=Ninja \
CMAKE_ARGS=-DHGRAPH_WARNINGS_AS_ERRORS=ON \
CMAKE_BUILD_PARALLEL_LEVEL=2 \
CMAKE_PREFIX_PATH="$hgraph_sdk" \
/opt/python/cp312-cp312/bin/python -m build \
--wheel --no-isolation --skip-dependency-check \
--outdir persistence-dist-raw extensions/persistence
- name: Repair persistence wheel for the glibc 2.28 floor
run: |
mkdir persistence-dist
auditwheel repair \
--plat manylinux_2_28_x86_64 \
--exclude libarrow.so.2500 \
--exclude libarrow_compute.so.2500 \
--exclude libarrow_acero.so.2500 \
--exclude libparquet.so.2500 \
--exclude libhgraph_runtime.so \
--exclude libhgraph_wiring.so \
--exclude libhgraph_stdlib.so \
--exclude libnanobind-abi3.so \
--wheel-dir persistence-dist \
persistence-dist-raw/*.whl
- name: Verify persistence wheel ABI and platform tag
run: |
/opt/python/cp312-cp312/bin/python - <<'PY'
from pathlib import Path
import subprocess
from packaging.tags import Tag
from packaging.utils import parse_wheel_filename
wheels = list(Path("persistence-dist").glob("*.whl"))
assert len(wheels) == 1, wheels
wheel = wheels[0]
assert wheel.name.startswith("hgraph_persistence-"), wheel.name
tags = parse_wheel_filename(wheel.name)[3]
expected = Tag("cp312", "abi3", "manylinux_2_28_x86_64")
assert expected in tags, (wheel.name, tags)
subprocess.check_call(["abi3audit", "--strict", str(wheel)])
PY
- name: Audit persistence wheel contents
run: >-
/opt/python/cp312-cp312/bin/python
extensions/persistence/tools/audit_distribution.py
"persistence-dist/*.whl"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: persistence-distribution-wheel-ubuntu-latest
path: persistence-dist/*.whl
if-no-files-found: error
- name: Install the matching persistence SDK
run: >-
/opt/python/cp312-cp312/bin/python -m pip install
--no-deps persistence-dist/*.whl
- name: Build Fabric stable ABI wheel
shell: bash
run: |
hgraph_sdk="$(/opt/python/cp312-cp312/bin/python -c 'import site; print(site.getsitepackages()[0])')"
CMAKE_GENERATOR=Ninja \
CMAKE_ARGS=-DHGRAPH_WARNINGS_AS_ERRORS=ON \
CMAKE_BUILD_PARALLEL_LEVEL=2 \
CMAKE_PREFIX_PATH="$hgraph_sdk" \
/opt/python/cp312-cp312/bin/python -m build \
--wheel --no-isolation --skip-dependency-check \
--outdir fabric-dist-raw extensions/fabric
- name: Repair Fabric wheel for the glibc 2.28 floor
run: |
mkdir fabric-dist
auditwheel repair \
--plat manylinux_2_28_x86_64 \
--exclude libarrow.so.2500 \
--exclude libarrow_compute.so.2500 \
--exclude libarrow_acero.so.2500 \
--exclude libparquet.so.2500 \
--exclude libhgraph_runtime.so \
--exclude libhgraph_wiring.so \
--exclude libhgraph_stdlib.so \
--exclude libhgraph_persistence.so \
--exclude libnanobind-abi3.so \
--wheel-dir fabric-dist \
fabric-dist-raw/*.whl
- name: Verify Fabric wheel ABI and platform tag
run: |
/opt/python/cp312-cp312/bin/python - <<'PY'
from pathlib import Path
import subprocess
from packaging.tags import Tag
from packaging.utils import parse_wheel_filename
wheels = list(Path("fabric-dist").glob("*.whl"))
assert len(wheels) == 1, wheels
wheel = wheels[0]
assert wheel.name.startswith("hgraph_fabric-"), wheel.name
tags = parse_wheel_filename(wheel.name)[3]
expected = Tag("cp312", "abi3", "manylinux_2_28_x86_64")
assert expected in tags, (wheel.name, tags)
subprocess.check_call(["abi3audit", "--strict", str(wheel)])
PY
- name: Audit Fabric wheel contents
run: >-
/opt/python/cp312-cp312/bin/python
extensions/fabric/tools/audit_distribution.py
"fabric-dist/*.whl"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: fabric-distribution-wheel-ubuntu-latest
path: fabric-dist/*.whl
if-no-files-found: error
- name: Install web wheel system dependencies
# The web extension's TLS stack builds against the container's
# OpenSSL headers; auditwheel then vendors the shared libraries
# (RFC 0024, packaging).
run: dnf install -y openssl-devel
- name: Build web stable ABI wheel
shell: bash
run: |
hgraph_sdk="$(/opt/python/cp312-cp312/bin/python -c 'import site; print(site.getsitepackages()[0])')"
CMAKE_GENERATOR=Ninja \
CMAKE_ARGS=-DHGRAPH_WARNINGS_AS_ERRORS=ON \
CMAKE_BUILD_PARALLEL_LEVEL=2 \
CMAKE_PREFIX_PATH="$hgraph_sdk" \
/opt/python/cp312-cp312/bin/python -m build \
--wheel --no-isolation --skip-dependency-check \
--outdir web-dist-raw extensions/web
- name: Repair web wheel for the glibc 2.28 floor
run: |
mkdir web-dist
auditwheel repair \
--plat manylinux_2_28_x86_64 \
--exclude libarrow.so.2500 \
--exclude libarrow_compute.so.2500 \
--exclude libarrow_acero.so.2500 \
--exclude libhgraph_runtime.so \
--exclude libhgraph_wiring.so \
--exclude libhgraph_stdlib.so \
--exclude libnanobind-abi3.so \
--wheel-dir web-dist \
web-dist-raw/*.whl
- name: Verify web wheel ABI and platform tag
run: |
/opt/python/cp312-cp312/bin/python - <<'PY'
from pathlib import Path
import subprocess
from packaging.tags import Tag
from packaging.utils import parse_wheel_filename
wheels = list(Path("web-dist").glob("*.whl"))
assert len(wheels) == 1, wheels
wheel = wheels[0]
assert wheel.name.startswith("hgraph_web-"), wheel.name
tags = parse_wheel_filename(wheel.name)[3]
expected = Tag("cp312", "abi3", "manylinux_2_28_x86_64")
assert expected in tags, (wheel.name, tags)
subprocess.check_call(["abi3audit", "--strict", str(wheel)])
PY
- name: Audit web wheel contents
run: >-
/opt/python/cp312-cp312/bin/python
extensions/web/tools/audit_distribution.py
"web-dist/*.whl"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: web-distribution-wheel-ubuntu-latest
path: web-dist/*.whl
if-no-files-found: error
build-sdist:
name: Build source distribution
needs:
- validate-release
- reuse-build
if: needs.reuse-build.outputs.run-id == ''
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.12"
- name: Build source distribution
run: |
python -m pip install --upgrade --only-binary=:all: \
"build==1.5.0" "scikit-build-core==1.0.3" \
"nanobind==2.13.0" "pyarrow==25.0.0"
python -m build --sdist
- name: Audit source distribution contents
run: python tools/audit_distribution.py "dist/*.tar.gz"
- name: Verify source distribution
shell: python
run: |
from pathlib import Path
import tomllib
version = tomllib.loads(Path("pyproject.toml").read_text())["project"]["version"]
archives = list(Path("dist").glob("*.tar.gz"))
assert len(archives) == 1, archives
assert archives[0].name == f"hgraph-{version}.tar.gz", archives[0].name
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: distribution-sdist
path: dist/*.tar.gz
if-no-files-found: error
- name: Build Kafka source distribution
run: >-
python -m build --sdist --no-isolation --skip-dependency-check
--outdir kafka-dist extensions/kafka
- name: Verify Kafka source distribution
shell: python
run: |
from pathlib import Path
import tarfile
import tomllib
version = tomllib.loads(
Path("extensions/kafka/pyproject.toml").read_text()
)["project"]["version"]
archives = list(Path("kafka-dist").glob("*.tar.gz"))
assert len(archives) == 1, archives
archive = archives[0]
assert archive.name == f"hgraph_kafka-{version}.tar.gz", archive.name
with tarfile.open(archive) as package:
names = set(package.getnames())
root = f"hgraph_kafka-{version}"
for required in (
"CMakeLists.txt",
"include/hgraph/kafka/service.h",
"python/hgraph_kafka/__init__.py",
):
assert f"{root}/{required}" in names, required
- name: Audit Kafka source distribution contents
run: python extensions/kafka/tools/audit_distribution.py "kafka-dist/*.tar.gz"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: kafka-distribution-sdist
path: kafka-dist/*.tar.gz
if-no-files-found: error
- name: Build analytics source distribution
run: >-
python -m build --sdist --no-isolation --skip-dependency-check
--outdir analytics-dist extensions/analytics
- name: Verify analytics source distribution
shell: python
run: |
from pathlib import Path
import tarfile
import tomllib
version = tomllib.loads(
Path("extensions/analytics/pyproject.toml").read_text()
)["project"]["version"]
archives = list(Path("analytics-dist").glob("*.tar.gz"))
assert len(archives) == 1, archives
archive = archives[0]
assert archive.name == f"hgraph_analytics-{version}.tar.gz", archive.name
with tarfile.open(archive) as package:
names = set(package.getnames())
root = f"hgraph_analytics-{version}"
for required in (
"CMakeLists.txt",
"include/hgraph/analytics/operators.h",
"python/hgraph_analytics/__init__.py",
):
assert f"{root}/{required}" in names, required
- name: Audit analytics source distribution contents
run: >-
python extensions/analytics/tools/audit_distribution.py
"analytics-dist/*.tar.gz"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: analytics-distribution-sdist
path: analytics-dist/*.tar.gz
if-no-files-found: error
- name: Build persistence source distribution
run: >-
python -m build --sdist --no-isolation --skip-dependency-check
--outdir persistence-dist extensions/persistence
- name: Verify persistence source distribution
shell: python
run: |
from pathlib import Path
import tarfile
import tomllib
version = tomllib.loads(
Path("extensions/persistence/pyproject.toml").read_text()
)["project"]["version"]
archives = list(Path("persistence-dist").glob("*.tar.gz"))
assert len(archives) == 1, archives
archive = archives[0]
assert archive.name == f"hgraph_persistence-{version}.tar.gz", archive.name
with tarfile.open(archive) as package:
names = set(package.getnames())
root = f"hgraph_persistence-{version}"
for required in (
"CMakeLists.txt",
"include/hgraph/persistence/frame_store.h",
"include/hgraph/persistence/recording_store.h",
"python/hgraph_persistence/__init__.py",
):
assert f"{root}/{required}" in names, required
- name: Audit persistence source distribution contents
run: >-
python extensions/persistence/tools/audit_distribution.py
"persistence-dist/*.tar.gz"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: persistence-distribution-sdist
path: persistence-dist/*.tar.gz
if-no-files-found: error
- name: Build Fabric source distribution
run: >-
python -m build --sdist --no-isolation --skip-dependency-check
--outdir fabric-dist extensions/fabric
- name: Verify Fabric source distribution
shell: python
run: |
from pathlib import Path
import tarfile
import tomllib
version = tomllib.loads(
Path("extensions/fabric/pyproject.toml").read_text()
)["project"]["version"]
archives = list(Path("fabric-dist").glob("*.tar.gz"))
assert len(archives) == 1, archives
archive = archives[0]
assert archive.name == f"hgraph_fabric-{version}.tar.gz", archive.name
with tarfile.open(archive) as package:
names = set(package.getnames())
root = f"hgraph_fabric-{version}"
for required in (
"CMakeLists.txt",
"include/hgraph/fabric/service.h",
"python/hgraph_fabric/__init__.py",
):
assert f"{root}/{required}" in names, required
- name: Audit Fabric source distribution contents
run: >-
python extensions/fabric/tools/audit_distribution.py
"fabric-dist/*.tar.gz"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: fabric-distribution-sdist
path: fabric-dist/*.tar.gz
if-no-files-found: error
- name: Build web source distribution
run: >-
python -m build --sdist --no-isolation --skip-dependency-check
--outdir web-dist extensions/web
- name: Verify web source distribution
shell: python
run: |
from pathlib import Path
import tarfile
import tomllib
version = tomllib.loads(
Path("extensions/web/pyproject.toml").read_text()
)["project"]["version"]
archives = list(Path("web-dist").glob("*.tar.gz"))
assert len(archives) == 1, archives
archive = archives[0]
assert archive.name == f"hgraph_web-{version}.tar.gz", archive.name
with tarfile.open(archive) as package:
names = set(package.getnames())
root = f"hgraph_web-{version}"
for required in (
"CMakeLists.txt",
"include/hgraph/web/service.h",
"python/hgraph_web/__init__.py",
):
assert f"{root}/{required}" in names, required
- name: Audit web source distribution contents
run: >-
python extensions/web/tools/audit_distribution.py
"web-dist/*.tar.gz"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: web-distribution-sdist
path: web-dist/*.tar.gz
if-no-files-found: error
test-windows-wheel:
name: Test Windows wheel artifacts
needs:
- reuse-build
- build-windows-wheel
if: needs.reuse-build.outputs.run-id == ''
uses: ./.github/workflows/test-platform-wheel.yml
with:
os: windows-latest
test-macos-wheel:
name: Test macOS wheel artifacts
needs:
- reuse-build
- build-macos-wheel
if: needs.reuse-build.outputs.run-id == ''
uses: ./.github/workflows/test-platform-wheel.yml
with:
os: macos-26
test-linux-wheel:
name: Test Linux wheel artifacts
needs:
- reuse-build
- build-linux-wheel
if: needs.reuse-build.outputs.run-id == ''
uses: ./.github/workflows/test-platform-wheel.yml
with:
os: ubuntu-latest
parity-smoke:
name: Differential parity smoke / Python 3.14
needs:
- reuse-build
- build-linux-wheel
if: needs.reuse-build.outputs.run-id == ''
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
with:
python-version: "3.14"
- name: Install parity controller
run: >-
python -m pip install --upgrade --only-binary=:all:
"uv==0.12.3" "hypothesis==6.165.2"
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: distribution-wheel-ubuntu-latest
path: dist
# The reference hgraph 0.5 serves the frame-recording scenarios
# natively; the candidate needs core + hgraph-persistence (RFC 0025).
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: persistence-distribution-wheel-ubuntu-latest
path: persistence-dist
- name: Restore released-hgraph traces
uses: actions/cache@caa296126883cff596d87d8935842f9db880ef25 # v5
with:
path: |
.parity/envs/reference-*
.parity/cache/reference-traces
key: parity-reference-${{ runner.os }}-py314-${{ hashFiles('tools/parity/**/*.py') }}
restore-keys: |
parity-reference-${{ runner.os }}-py314-
- name: Run fixed corpus and deterministic generated matrix
run: >-
python -m tools.parity campaign
--profile pr
--seed 20260726
--candidate-wheel dist/*.whl
--candidate-extra-wheel persistence-dist/*.whl
--output-dir parity-results
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
if: always()
with:
name: parity-smoke
path: parity-results
if-no-files-found: error
publish:
name: Publish hgraph to PyPI
if: >-
always() &&
github.ref_type == 'tag' &&
needs.validate-release.result == 'success' &&
needs.reuse-build.result == 'success' &&
(
needs.reuse-build.outputs.run-id != '' ||
(
needs.build-sdist.result == 'success' &&
needs.test-windows-wheel.result == 'success' &&
needs.test-macos-wheel.result == 'success' &&
needs.test-linux-wheel.result == 'success'
)
)
needs:
- validate-release
- reuse-build
- build-sdist
- test-windows-wheel
- test-macos-wheel
- test-linux-wheel
- build-linux-wheel
runs-on: ubuntu-latest
environment:
name: release
url: https://pypi.org/p/hgraph
permissions:
actions: read
id-token: write
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
pattern: distribution-*
path: dist
merge-multiple: true
github-token: ${{ github.token }}
run-id: ${{ needs.reuse-build.outputs.run-id || github.run_id }}
- name: Restamp distributions to the tag version
env:
RELEASE_TAG: ${{ github.ref_name }}
run: python tools/restamp_distribution.py dist "$RELEASE_TAG"
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
with:
packages-dir: dist
publish-kafka:
name: Publish hgraph-kafka to PyPI
if: >-
always() &&
github.ref_type == 'tag' &&
needs.validate-release.result == 'success' &&
needs.reuse-build.result == 'success' &&
(
needs.reuse-build.outputs.run-id != '' ||
(
needs.build-sdist.result == 'success' &&
needs.test-windows-wheel.result == 'success' &&
needs.test-macos-wheel.result == 'success' &&
needs.test-linux-wheel.result == 'success'
)
)
needs:
- validate-release
- reuse-build
- build-sdist
- test-windows-wheel
- test-macos-wheel
- test-linux-wheel
- build-linux-wheel
runs-on: ubuntu-latest
environment:
name: release
url: https://pypi.org/p/hgraph-kafka
permissions:
actions: read
id-token: write
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
pattern: kafka-distribution-*
path: dist
merge-multiple: true
github-token: ${{ github.token }}
run-id: ${{ needs.reuse-build.outputs.run-id || github.run_id }}
- name: Restamp Kafka distributions to the tag version
env:
RELEASE_TAG: ${{ github.ref_name }}
run: python tools/restamp_distribution.py dist "$RELEASE_TAG"
- name: Publish Kafka package distributions to PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
with:
packages-dir: dist
publish-analytics:
name: Publish hgraph-analytics to PyPI
if: >-
always() &&
github.ref_type == 'tag' &&
needs.validate-release.result == 'success' &&
needs.reuse-build.result == 'success' &&
(
needs.reuse-build.outputs.run-id != '' ||
(
needs.build-sdist.result == 'success' &&
needs.test-windows-wheel.result == 'success' &&
needs.test-macos-wheel.result == 'success' &&
needs.test-linux-wheel.result == 'success'
)
)
needs:
- validate-release
- reuse-build
- build-sdist
- test-windows-wheel
- test-macos-wheel
- test-linux-wheel
- build-linux-wheel
runs-on: ubuntu-latest
environment:
name: release
url: https://pypi.org/p/hgraph-analytics
permissions:
actions: read
id-token: write
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
pattern: analytics-distribution-*
path: dist
merge-multiple: true
github-token: ${{ github.token }}
run-id: ${{ needs.reuse-build.outputs.run-id || github.run_id }}
- name: Restamp analytics distributions to the tag version
env:
RELEASE_TAG: ${{ github.ref_name }}
run: python tools/restamp_distribution.py dist "$RELEASE_TAG"
- name: Publish analytics package distributions to PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
with:
packages-dir: dist
publish-web:
name: Publish hgraph-web to PyPI
if: >-
always() &&
github.ref_type == 'tag' &&
needs.validate-release.result == 'success' &&
needs.reuse-build.result == 'success' &&
(
needs.reuse-build.outputs.run-id != '' ||
(
needs.build-sdist.result == 'success' &&
needs.test-windows-wheel.result == 'success' &&
needs.test-macos-wheel.result == 'success' &&
needs.test-linux-wheel.result == 'success'
)
)
needs:
- validate-release
- reuse-build
- build-sdist
- test-windows-wheel
- test-macos-wheel
- test-linux-wheel
- build-linux-wheel
runs-on: ubuntu-latest
environment:
name: release
url: https://pypi.org/p/hgraph-web
permissions:
actions: read
id-token: write
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
pattern: web-distribution-*
path: dist
merge-multiple: true
github-token: ${{ github.token }}
run-id: ${{ needs.reuse-build.outputs.run-id || github.run_id }}
- name: Restamp web distributions to the tag version
env:
RELEASE_TAG: ${{ github.ref_name }}
run: python tools/restamp_distribution.py dist "$RELEASE_TAG"
- name: Publish web package distributions to PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
with:
packages-dir: dist
publish-persistence:
name: Publish hgraph-persistence to PyPI
if: >-
always() &&
github.ref_type == 'tag' &&
needs.validate-release.result == 'success' &&
needs.reuse-build.result == 'success' &&
(
needs.reuse-build.outputs.run-id != '' ||
(
needs.build-sdist.result == 'success' &&
needs.test-windows-wheel.result == 'success' &&
needs.test-macos-wheel.result == 'success' &&
needs.test-linux-wheel.result == 'success'
)
)
needs:
- validate-release
- reuse-build
- build-sdist
- test-windows-wheel
- test-macos-wheel
- test-linux-wheel
- build-linux-wheel
runs-on: ubuntu-latest
environment:
name: release
url: https://pypi.org/p/hgraph-persistence
permissions:
actions: read
id-token: write
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
pattern: persistence-distribution-*
path: dist
merge-multiple: true
github-token: ${{ github.token }}
run-id: ${{ needs.reuse-build.outputs.run-id || github.run_id }}
- name: Restamp persistence distributions to the tag version
env:
RELEASE_TAG: ${{ github.ref_name }}
run: python tools/restamp_distribution.py dist "$RELEASE_TAG"
- name: Publish persistence package distributions to PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
with:
packages-dir: dist
publish-fabric:
name: Publish hgraph-fabric to PyPI
if: >-
always() &&
github.ref_type == 'tag' &&
needs.validate-release.result == 'success' &&
needs.reuse-build.result == 'success' &&
(
needs.reuse-build.outputs.run-id != '' ||
(
needs.build-sdist.result == 'success' &&
needs.test-windows-wheel.result == 'success' &&
needs.test-macos-wheel.result == 'success' &&
needs.test-linux-wheel.result == 'success'
)
)
needs:
- validate-release
- reuse-build
- build-sdist
- test-windows-wheel
- test-macos-wheel
- test-linux-wheel
- build-linux-wheel
runs-on: ubuntu-latest
environment:
name: release
url: https://pypi.org/p/hgraph-fabric
permissions:
actions: read
id-token: write
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
pattern: fabric-distribution-*
path: dist
merge-multiple: true
github-token: ${{ github.token }}
run-id: ${{ needs.reuse-build.outputs.run-id || github.run_id }}
- name: Restamp Fabric distributions to the tag version
env:
RELEASE_TAG: ${{ github.ref_name }}
run: python tools/restamp_distribution.py dist "$RELEASE_TAG"
- name: Publish Fabric package distributions to PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
with:
packages-dir: dist