Skip to content

Commit 2b11c59

Browse files
committed
ci: add GitHub Actions CI/CD for v0.6.5
- CI workflow: test matrix (Linux/macOS/Windows x py3.9-3.13), GPU features, full features, typecheck, lint - Release workflow: multi-arch wheels, clean-venv verification, atomic PyPI publish - Bump version to 0.6.5 in Cargo.toml, pyproject.toml, __init__.py - Fix MCP test skip logic for missing grpc feature
0 parents  commit 2b11c59

6 files changed

Lines changed: 2667 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, release/*]
6+
pull_request:
7+
branches: [main]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
12+
jobs:
13+
test:
14+
name: Test (${{ matrix.os }}, py${{ matrix.python }})
15+
runs-on: ${{ matrix.os }}
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
os: [ubuntu-latest, macos-latest, windows-latest]
20+
python: ["3.9", "3.10", "3.11", "3.12", "3.13"]
21+
include:
22+
- os: ubuntu-latest
23+
target: x86_64-unknown-linux-gnu
24+
- os: macos-latest
25+
target: universal2-apple-darwin
26+
- os: windows-latest
27+
target: x86_64-pc-windows-msvc
28+
steps:
29+
- uses: actions/checkout@v4
30+
31+
- name: Install Rust
32+
uses: dtolnay/rust-toolchain@stable
33+
with:
34+
target: ${{ matrix.target }}
35+
36+
- name: Set up Python ${{ matrix.python }}
37+
uses: actions/setup-python@v5
38+
with:
39+
python-version: ${{ matrix.python }}
40+
41+
- name: Cache cargo
42+
uses: actions/cache@v4
43+
with:
44+
path: |
45+
~/.cargo/bin
46+
~/.cargo/registry/index
47+
~/.cargo/registry/cache
48+
target
49+
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
50+
51+
- name: Install maturin
52+
run: pip install maturin
53+
54+
- name: Build (debug)
55+
run: maturin develop
56+
env:
57+
RUSTFLAGS: -C target-cpu=native
58+
59+
- name: Run tests
60+
run: python -m pytest python/tests/ -q --tb=short -x
61+
timeout-minutes: 30
62+
63+
test-gpu:
64+
name: GPU Features Test
65+
runs-on: ubuntu-latest
66+
steps:
67+
- uses: actions/checkout@v4
68+
69+
- name: Install Rust
70+
uses: dtolnay/rust-toolchain@stable
71+
72+
- name: Set up Python 3.12
73+
uses: actions/setup-python@v5
74+
with:
75+
python-version: "3.12"
76+
77+
- name: Install maturin
78+
run: pip install maturin
79+
80+
- name: Build with CUDA feature
81+
run: maturin develop --features cuda
82+
env:
83+
RUSTFLAGS: -C target-cpu=native
84+
85+
- name: Test CUDA available
86+
run: python -c "import qector_decoder_v3 as qd; print('CUDA:', qd.cuda_is_available())"
87+
88+
- name: Build with OpenCL feature
89+
run: maturin develop --features opencl
90+
env:
91+
RUSTFLAGS: -C target-cpu=native
92+
93+
- name: Test OpenCL available
94+
run: python -c "import qector_decoder_v3 as qd; print('OpenCL:', qd.opencl_is_available())"
95+
96+
test-full:
97+
name: Full Features Test
98+
runs-on: ubuntu-latest
99+
steps:
100+
- uses: actions/checkout@v4
101+
102+
- name: Install Rust
103+
uses: dtolnay/rust-toolchain@stable
104+
105+
- name: Set up Python 3.12
106+
uses: actions/setup-python@v5
107+
with:
108+
python-version: "3.12"
109+
110+
- name: Install maturin
111+
run: pip install maturin
112+
113+
- name: Build with full features
114+
run: maturin develop --features full
115+
env:
116+
RUSTFLAGS: -C target-cpu=native
117+
118+
- name: Test gRPC/MCP server
119+
run: |
120+
python -c "import qector_decoder_v3 as qd; assert qd.run_mcp_server is not None; assert qd.run_grpc_server is not None; print('gRPC/MCP available')"
121+
122+
typecheck:
123+
name: Type Check
124+
runs-on: ubuntu-latest
125+
steps:
126+
- uses: actions/checkout@v4
127+
128+
- name: Set up Python 3.12
129+
uses: actions/setup-python@v5
130+
with:
131+
python-version: "3.12"
132+
133+
- name: Install deps
134+
run: pip install mypy ruff
135+
136+
- name: Run mypy
137+
run: mypy python/qector_decoder_v3 --ignore-missing-imports
138+
139+
- name: Run ruff
140+
run: ruff check python/qector_decoder_v3 python/tests
141+
142+
lint:
143+
name: Lint (Rust)
144+
runs-on: ubuntu-latest
145+
steps:
146+
- uses: actions/checkout@v4
147+
148+
- name: Install Rust
149+
uses: dtolnay/rust-toolchain@stable
150+
with:
151+
components: clippy, rustfmt
152+
153+
- name: Run cargo fmt
154+
run: cargo fmt --all -- --check
155+
156+
- name: Run cargo clippy
157+
run: cargo clippy --all-targets --all-features -- -D warnings

.github/workflows/release.yml

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
name: Release Wheels
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: "Version (e.g. 0.6.5)"
8+
required: true
9+
type: string
10+
dry_run:
11+
description: "Build only, don't publish"
12+
required: false
13+
type: boolean
14+
default: true
15+
release:
16+
types: [published]
17+
18+
permissions:
19+
contents: read
20+
packages: write
21+
22+
env:
23+
CARGO_TERM_COLOR: always
24+
25+
jobs:
26+
build-wheels:
27+
name: Build Wheels (${{ matrix.os }}, ${{ matrix.target }})
28+
runs-on: ${{ matrix.os }}
29+
strategy:
30+
fail-fast: false
31+
matrix:
32+
include:
33+
- os: ubuntu-latest
34+
target: x86_64-unknown-linux-gnu
35+
python: "3.12"
36+
manylinux: manylinux_2_28_x86_64
37+
- os: ubuntu-latest
38+
target: aarch64-unknown-linux-gnu
39+
python: "3.12"
40+
manylinux: manylinux_2_28_aarch64
41+
- os: macos-latest
42+
target: universal2-apple-darwin
43+
python: "3.12"
44+
manylinux: ""
45+
- os: windows-latest
46+
target: x86_64-pc-windows-msvc
47+
python: "3.12"
48+
manylinux: ""
49+
steps:
50+
- uses: actions/checkout@v4
51+
52+
- name: Install Rust
53+
uses: dtolnay/rust-toolchain@stable
54+
with:
55+
target: ${{ matrix.target }}
56+
57+
- name: Set up Python ${{ matrix.python }}
58+
uses: actions/setup-python@v5
59+
with:
60+
python-version: ${{ matrix.python }}
61+
62+
- name: Cache cargo
63+
uses: actions/cache@v4
64+
with:
65+
path: |
66+
~/.cargo/bin
67+
~/.cargo/registry/index
68+
~/.cargo/registry/cache
69+
target
70+
key: ${{ runner.os }}-cargo-release-${{ hashFiles('**/Cargo.lock') }}
71+
72+
- name: Install maturin
73+
run: pip install maturin
74+
75+
- name: Build wheel
76+
id: build
77+
run: |
78+
maturin build --release --strip --target ${{ matrix.target }} --out dist/
79+
env:
80+
RUSTFLAGS: -C target-cpu=native
81+
82+
- name: Verify wheel
83+
run: |
84+
ls -la dist/
85+
python -m pip install dist/*.whl
86+
python -c "
87+
import qector_decoder_v3 as qd
88+
print('Version:', qd.__version__)
89+
print('FastUF:', hasattr(qd, 'FastUnionFindDecoder'))
90+
print('Blossom:', hasattr(qd, 'BlossomDecoder'))
91+
print('Batch:', hasattr(qd, 'BatchDecoder'))
92+
print('CUDA:', hasattr(qd, 'CUDABatchDecoder'))
93+
print('OpenCL:', hasattr(qd, 'OpenCLBatchDecoder'))
94+
print('gRPC:', hasattr(qd, 'run_grpc_server'))
95+
print('MCP:', hasattr(qd, 'run_mcp_server'))
96+
"
97+
shell: bash
98+
99+
- name: Test wheel in clean venv
100+
run: |
101+
python -m venv .venv_test
102+
.venv_test/bin/pip install dist/*.whl
103+
.venv_test/bin/python -c "
104+
import qector_decoder_v3 as qd
105+
import numpy as np
106+
# Test core decoders
107+
dec = qd.FastUnionFindDecoder([[0,1]], 2)
108+
corr = dec.decode(np.array([1], dtype=np.uint8))
109+
print('FastUF decode:', corr)
110+
# Test Blossom
111+
dec2 = qd.BlossomDecoder([[0,1,2,3]], 4)
112+
corr2 = dec2.decode(np.array([1], dtype=np.uint8))
113+
print('Blossom decode:', corr2)
114+
# Test Batch
115+
dec3 = qd.BatchDecoder([[0,1]], 2)
116+
corr3 = dec3.batch_decode(np.array([[1]], dtype=np.uint8))
117+
print('Batch decode:', corr3)
118+
print('All decoder tests passed')
119+
"
120+
shell: bash
121+
122+
- name: Upload wheel artifact
123+
uses: actions/upload-artifact@v4
124+
with:
125+
name: wheels-${{ matrix.os }}-${{ matrix.target }}
126+
path: dist/*.whl
127+
retention-days: 7
128+
129+
publish:
130+
name: Publish to PyPI
131+
needs: build-wheels
132+
if: github.event.inputs.dry_run != 'true' || github.event_name == 'release'
133+
runs-on: ubuntu-latest
134+
permissions:
135+
id-token: write
136+
contents: read
137+
steps:
138+
- name: Download all wheels
139+
uses: actions/download-artifact@v4
140+
with:
141+
pattern: wheels-*
142+
path: dist/
143+
merge-multiple: true
144+
145+
- name: Verify wheels
146+
run: |
147+
ls -la dist/
148+
pip install twine
149+
twine check dist/*
150+
151+
- name: Publish to PyPI
152+
uses: pypa/gh-action-pypi-publish@release/v1
153+
with:
154+
packages-dir: dist/
155+
verbose: true
156+
157+
tag-version:
158+
name: Tag Version
159+
needs: build-wheels
160+
if: github.event.inputs.dry_run != 'true' && github.event_name == 'workflow_dispatch'
161+
runs-on: ubuntu-latest
162+
permissions:
163+
contents: write
164+
steps:
165+
- uses: actions/checkout@v4
166+
with:
167+
fetch-depth: 0
168+
169+
- name: Configure git
170+
run: |
171+
git config user.name "github-actions[bot]"
172+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
173+
174+
- name: Create and push tag
175+
env:
176+
VERSION: ${{ github.event.inputs.version }}
177+
run: |
178+
git tag -a v${VERSION} -m "Release v${VERSION}"
179+
git push origin v${VERSION}
180+
181+
- name: Create GitHub Release
182+
uses: softprops/action-gh-release@v1
183+
with:
184+
tag_name: v${{ github.event.inputs.version }}
185+
generate_release_notes: true
186+
files: dist/*.whl

0 commit comments

Comments
 (0)