1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# Release workflow — dual publish on version tags.
#
# Tag `vX.Y.Z` on **main** (after merging rust_dev) triggers:
# 1. crates.io — pure-Rust crate `bobine` (default features, no pyo3)
# 2. PyPI — full Python package `bobine` via maturin (extension-module)
#
# Required secrets / configuration:
# - secrets.CARGO_REGISTRY_TOKEN → crates.io API token
# - PyPI trusted publisher configured for this workflow + environment `pypi`
# (Project: bobine · Workflow: release.yml · Environment: pypi)
name: Release
on:
push:
tags:
workflow_dispatch:
permissions:
contents: read
jobs:
# ------------------------------------------------------------------
# 1. crates.io — Rust library (no Python bindings)
# ------------------------------------------------------------------
crates-io:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- name: Tag matches Cargo.toml version
run: |
tag="${GITHUB_REF_NAME#v}"
ver=$(grep -m1 '^version' Cargo.toml | sed 's/.*"\(.*\)"/\1/')
test "$tag" = "$ver" || { echo "tag v$tag != Cargo.toml $ver"; exit 1; }
- name: Verify default build is Python-free
run: cargo check --locked
- name: Download onnxruntime (CPU — lib tests abort without a dylib)
run: |
curl -sL --max-time 300 -o /tmp/ort.tgz \
https://github.com/microsoft/onnxruntime/releases/download/v1.28.1/onnxruntime-linux-x64-1.28.1.tgz
mkdir -p /tmp/ort && tar xzf /tmp/ort.tgz -C /tmp/ort
- name: Run library unit tests (integration needs onnxruntime)
env:
ORT_DYLIB_PATH: /tmp/ort/onnxruntime-linux-x64-1.28.1/lib/libonnxruntime.so.1.28.1
run: cargo test --locked --lib
- name: Publish to crates.io
# Tags publish; manual dispatch is a dry-run (check + test only).
if: startsWith(github.ref, 'refs/tags/v')
run: cargo publish --locked
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
# ------------------------------------------------------------------
# 2. PyPI — Python wheel matrix via maturin (trusted publishing)
# ------------------------------------------------------------------
pypi:
# ubuntu + windows x86_64, macOS arm64 (native runners — no cross builds).
# No Intel mac (deprecated runners), no other exotic targets.
strategy:
fail-fast: false
matrix:
platform:
- runner: ubuntu-22.04
target: x86_64
- runner: windows-latest
target: x64
- runner: macos-latest
target: aarch64
runs-on: ${{ matrix.platform.runner }}
permissions:
id-token: write # PyPI trusted publishing
steps:
- uses: actions/checkout@v6
- name: Build wheel (sdist on linux x86_64 too)
uses: PyO3/maturin-action@v1
with:
target: ${{ matrix.platform.target }}
args: --release --out dist --locked --features extension-module
# 2_28, not auto: auto resolves to CentOS 7-based manylinux2014
# (OpenSSL 1.0.2 < openssl-sys minimum 1.1.0). Ignored on win/mac.
manylinux: ${{ matrix.platform.manylinux || '2_28' }}
sccache: true
# Linux wheels build in a manylinux container: ort-sys build-deps
# on ureq/native-tls → openssl-sys needs headers there (no-op on
# windows/macos runners).
before-script-linux: yum install -y openssl-devel
- name: Upload wheels
uses: actions/upload-artifact@v6
with:
name: wheels-${{ matrix.platform.runner }}-${{ matrix.platform.target }}
path: dist
# ------------------------------------------------------------------
# 3. sdist + PyPI upload (single verified copy — no merge-multiple)
# ------------------------------------------------------------------
pypi-publish:
needs:
runs-on: ubuntu-22.04
environment: pypi
permissions:
id-token: write
steps:
- uses: actions/checkout@v6
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
command: sdist
args: --out dist
- name: Download wheels (per-artifact dirs — never merge-multiple,
# see legacy release postmortem: identical paths across artifacts
# corrupt the merged zip)
uses: actions/download-artifact@v6
with:
pattern: wheels-*
path: dist
- name: Flatten wheel directories (drop emptied artifact dirs —
# the publisher rejects any non-distribution entry in dist/)
run: |
find dist -name '*.whl' -exec mv {} dist/ \;
find dist -mindepth 1 -type d -empty -delete
ls -la dist/
- name: Publish to PyPI
# Tags publish; manual dispatch stops after building wheels.
if: startsWith(github.ref, 'refs/tags/v')
uses: pypa/gh-action-pypi-publish@release/v1