Merge remote-tracking branch 'origin/main' into feat/ruview-auth-cognitum-oauth-verifier

# Conflicts:
#	v2/crates/wifi-densepose-sensing-server/src/main.rs
This commit is contained in:
Dragan Spiridonov
2026-07-24 15:26:15 +02:00
71 changed files with 10058 additions and 345 deletions
+24 -4
View File
@@ -13,10 +13,26 @@
# 1. cut tag `v1.99.0-pip` → publishes the tombstone wheel first
# 2. cut tag `v2.0.0-pip` → publishes the PyO3 v2 wheel matrix
#
# Publishes via the `PYPI_API_TOKEN` GitHub Actions secret. The
# token-refresh runbook (GCP Secret Manager → gh secret set) lives in
# docs/integrations/pypi-release.md so KICS does not flag the
# secret name as a generic-secret literal in the workflow.
# Publishes via the `PYPI_API_TOKEN` GitHub Actions secret (API-token
# auth). This is the ACTIVE, working publish path — the token is sourced
# fresh from GCP Secret Manager per the runbook in
# docs/integrations/pypi-release.md (GCP Secret Manager → gh secret set),
# which also keeps KICS from flagging the secret name as a generic-secret
# literal here.
#
# TODO(ADR-184 P1b): migrate to PyPI OIDC Trusted Publishing to remove
# this rotatable/expire-able credential. That switch is GATED on a manual
# pypi.org step no CLI/agent can perform: the repo owner must register a
# Trusted Publisher on pypi.org for owner=ruvnet / repo=RuView /
# workflow=pip-release.yml (BOTH the wifi-densepose and ruview projects;
# ruview as a pending publisher) — see docs/adr/ADR-184-*.md. Do NOT grant
# the OIDC id-token write permission before that registration exists, or
# publishing fails with "no trusted publisher configured" — a silent
# regression the `Verify fix markers` guard `RuView#786-pypi-token-auth`
# exists to catch (it forbids that permission string in this file). When
# the owner confirms both entries are live, do the OIDC switch as a
# dedicated follow-up commit (drop `password:`, add the OIDC id-token
# permission + `environment: pypi`) so there is no capability gap between.
#
# Q3 (witness hash v2 — open in ADR-117 §11.3) MUST be resolved
# before the first v2.0.0 publish. When v2 lands, add a parallel
@@ -241,6 +257,8 @@ jobs:
mkdir -p dist
find dist-staging -type f \( -name '*.whl' -o -name '*.tar.gz' \) -exec cp -v {} dist/ \;
ls -lh dist/
# API-token auth (active path). See TODO(ADR-184 P1b) in the header
# before replacing `password:` with the OIDC id-token permission.
- name: Publish to TestPyPI (dry-run target)
if: github.event_name == 'workflow_dispatch' && inputs.publish_to == 'testpypi'
uses: pypa/gh-action-pypi-publish@release/v1
@@ -274,6 +292,8 @@ jobs:
with:
name: tombstone
path: dist
# API-token auth (active path). See TODO(ADR-184 P1b) in the header
# before replacing `password:` with the OIDC id-token permission.
- name: Publish to TestPyPI (dry-run target)
if: github.event_name == 'workflow_dispatch' && inputs.publish_to == 'testpypi'
uses: pypa/gh-action-pypi-publish@release/v1
+170
View File
@@ -0,0 +1,170 @@
# Python Package CI — gates the `python/` PyO3+maturin wheel (`wifi-densepose`).
#
# ADR-117 (pip modernization) + ADR-185 (SOTA extras). Unlike the frozen
# `archive/v1/` Python app — which is `continue-on-error: true` in ci.yml
# because it is reference-only — the `python/` package is an actively-shipped
# PyPI wheel (published by pip-release.yml). Before this workflow, `python/`
# had ZERO per-PR coverage: pip-release.yml only fires on release triggers
# (tags / dispatch), so a PR could break the wheel build, break a native-Rust
# parity test, or blow the wheel-size budget and nothing in the normal gating
# CI would notice until release day. This workflow closes that gap.
#
# Path-scoped as a DEDICATED workflow rather than a job inside ci.yml. That is
# this repo's own convention for component-scoped CI (cf. firmware-ci.yml,
# sensing-server-docker.yml, bfld-mqtt-integration.yml — all separate files
# with `paths:` triggers). GitHub only supports workflow-level `paths:`, not
# per-job path filters, and no workflow in this repo uses a change-detection
# action (dorny/paths-filter, tj-actions/changed-files) — so the idiomatic,
# no-new-dependency way to scope to `python/**` is a standalone workflow. It
# simply does not run on unrelated PRs.
name: Python Package CI
on:
push:
branches:
- '**'
# NOTE: kept in sync with the pull_request paths below. GitHub Actions
# does not reliably support YAML anchors in workflow files, so the two
# lists are duplicated deliberately rather than aliased.
paths:
- 'python/**'
- 'v2/crates/wifi-densepose-core/**'
- 'v2/crates/wifi-densepose-vitals/**'
- 'v2/crates/wifi-densepose-bfld/**'
- 'v2/crates/wifi-densepose-aether/**'
- 'v2/crates/wifi-densepose-mat/**'
- 'v2/crates/wifi-densepose-train/**'
- 'v2/crates/wifi-densepose-signal/**'
- '.github/workflows/python-ci.yml'
pull_request:
paths:
- 'python/**'
- 'v2/crates/wifi-densepose-core/**'
- 'v2/crates/wifi-densepose-vitals/**'
- 'v2/crates/wifi-densepose-bfld/**'
- 'v2/crates/wifi-densepose-aether/**'
- 'v2/crates/wifi-densepose-mat/**'
- 'v2/crates/wifi-densepose-train/**'
- 'v2/crates/wifi-densepose-signal/**'
- '.github/workflows/python-ci.yml'
workflow_dispatch:
permissions:
contents: read
concurrency:
group: python-ci-${{ github.ref }}
cancel-in-progress: true
jobs:
# Build the wheel with ALL SOTA features and run the full parity suite.
# `--features sota` = aether + meridian + mat, so the compiled feature
# submodules (wifi_densepose.aether / .meridian / .mat) exist and their
# SHA-256 parity tests against the native-Rust reference actually run
# (test_aether.py / test_meridian.py / test_mat.py import those submodules
# at collection time — without the features they would error, not skip).
parity-tests:
name: Wheel + parity tests (features=sota)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# The python/ crate path-deps v2/crates/* and (transitively via
# train) the vendored ruvector submodule — recursive checkout keeps
# those path deps resolvable, matching the rust-tests job in ci.yml.
submodules: recursive
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo (Swatinem/rust-cache)
uses: Swatinem/rust-cache@v2
with:
workspaces: |
v2
python
# Fast-fail with per-crate attribution BEFORE the heavier maturin build,
# so a break in a binding-backing crate is reported as "this crate failed
# to build" rather than buried in a maturin link error. These are the
# crates the [aether]/[mat]/[meridian] compiled extras link.
- name: Build binding-backing crates
working-directory: v2
env:
CARGO_PROFILE_DEV_DEBUG: "0"
run: cargo build -p wifi-densepose-aether -p wifi-densepose-mat -p wifi-densepose-train
# maturin develop needs an active virtualenv; create one and expose it
# to the later steps via GITHUB_PATH so `maturin` / `pytest` resolve to
# it. Test deps: pytest-asyncio (client tests are async, asyncio_mode
# auto), numpy (test_bfld), websockets + paho-mqtt (the [client] extra
# used by test_client_*).
- name: Create venv + install maturin and test deps
run: |
python -m venv .venv
. .venv/bin/activate
python -m pip install --upgrade pip
pip install "maturin>=1.7,<2.0" pytest pytest-asyncio numpy websockets paho-mqtt
echo "VIRTUAL_ENV=$PWD/.venv" >> "$GITHUB_ENV"
echo "$PWD/.venv/bin" >> "$GITHUB_PATH"
- name: Build + install wheel (maturin develop --features sota)
working-directory: python
env:
CARGO_PROFILE_DEV_DEBUG: "0"
run: maturin develop --features sota
- name: Run parity + binding tests
run: pytest python/tests/ -q
# Numeric enforcement of the ADR-117 §5.4 default-wheel budget. A fix-marker
# can guard the CONFIG that keeps the wheel small (empty default features,
# optional SOTA deps, strip=true — see RuView#1387-default-wheel-budget-config
# in scripts/fix-markers.json) but it cannot measure bytes. This job builds
# the DEFAULT (no-features) wheel and fails if it exceeds the budget — the
# real guard against a dependency silently ballooning the shipped wheel.
wheel-size-budget:
name: Default wheel <= 5 MiB (ADR-117 §5.4)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.11'
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo (Swatinem/rust-cache)
uses: Swatinem/rust-cache@v2
with:
workspaces: python
- name: Install maturin
run: python -m pip install --upgrade pip "maturin>=1.7,<2.0"
- name: Build default wheel and assert size budget
working-directory: python
run: |
set -euo pipefail
maturin build --release --out dist
whl="$(ls dist/*.whl | head -1)"
bytes="$(stat -c%s "$whl")"
limit=$((5 * 1024 * 1024)) # ADR-117 §5.4: 5 MiB
printf 'Default wheel: %s = %s bytes (%s MiB); budget = %s bytes\n' \
"$whl" "$bytes" "$((bytes / 1024 / 1024))" "$limit"
if [ "$bytes" -gt "$limit" ]; then
echo "::error::default wheel is $bytes bytes, over the ADR-117 §5.4 $limit-byte (5 MiB) budget"
exit 1
fi
echo "Default wheel is within the 5 MiB budget."