mirror of
https://github.com/ruvnet/RuView
synced 2026-07-24 17:43:20 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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."
|
||||
Reference in New Issue
Block a user