Skip to content

Commit 1e7ed6d

Browse files
Merge branch 'master' into dependabot/pip/pytest-9.0.2
Signed-off-by: Jackson Antonio do Prado Lima <jacksonpradolima@users.noreply.github.com>
2 parents 76a0189 + 661a6f0 commit 1e7ed6d

File tree

21 files changed

+1884
-899
lines changed

21 files changed

+1884
-899
lines changed

.github/workflows/benchmarks.yml

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
name: Benchmarks
2+
3+
on:
4+
schedule:
5+
- cron: "0 6 * * 1"
6+
workflow_dispatch:
7+
inputs:
8+
run_python_backend:
9+
description: "Run Python backend baseline (skips Rust build)"
10+
required: false
11+
default: false
12+
type: boolean
13+
14+
jobs:
15+
rust-benchmark:
16+
name: Rust backend
17+
runs-on: ubuntu-latest
18+
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v6
22+
23+
- name: Set up Python
24+
uses: actions/setup-python@v6
25+
with:
26+
python-version: "3.13"
27+
28+
- name: Install uv
29+
uses: astral-sh/uv-action@v3
30+
with:
31+
version: "latest"
32+
python-version: "3.13"
33+
34+
- name: Sync dependencies
35+
run: |
36+
set -euo pipefail
37+
uv venv .venv --python 3.13 --allow-existing
38+
uv sync --frozen --extra dev
39+
40+
- name: Install Rust toolchain
41+
uses: dtolnay/rust-toolchain@stable
42+
43+
- name: Build Rust backend
44+
run: make rust-build
45+
46+
- name: Run benchmark (Rust backend)
47+
env:
48+
GSPPY_BACKEND: rust
49+
run: |
50+
set -euo pipefail
51+
uv run python benchmarks/bench_support.py \
52+
--n_tx 5000 \
53+
--tx_len 6 \
54+
--vocab 200 \
55+
--min_support 0.2 \
56+
--warmup \
57+
| tee benchmark_rust.log
58+
59+
- name: Extract metrics
60+
id: metrics_rust
61+
run: |
62+
set -euo pipefail
63+
python - <<'PY'
64+
import json
65+
import pathlib
66+
import re
67+
import time
68+
69+
log_path = pathlib.Path("benchmark_rust.log")
70+
log = log_path.read_text()
71+
72+
py_match = re.search(r"Python:\s*([0-9.]+)s", log)
73+
rs_match = re.search(r"Rust:\s*([0-9.]+)s", log)
74+
75+
data = {
76+
"timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
77+
"backend": "rust",
78+
"n_tx": 5000,
79+
"tx_len": 6,
80+
"vocab": 200,
81+
"min_support": 0.2,
82+
}
83+
84+
if py_match:
85+
data["python_time_s"] = float(py_match.group(1))
86+
if rs_match:
87+
data["rust_time_s"] = float(rs_match.group(1))
88+
89+
if py_match and rs_match:
90+
py_time = float(py_match.group(1))
91+
rs_time = float(rs_match.group(1))
92+
if rs_time > 0:
93+
data["speedup_x"] = py_time / rs_time
94+
if py_time > 0: 0:
95+
data["improvement_pct"] = (py_time - rs_time) / py_time * 100
96+
else:
97+
data["improvement_pct"] = None
98+
99+
pathlib.Path("benchmark_rust.json").write_text(json.dumps(data, indent=2))
100+
PY
101+
102+
- name: Upload benchmark artifacts
103+
uses: actions/upload-artifact@v6
104+
with:
105+
name: rust-benchmark
106+
path: |
107+
benchmark_rust.log
108+
benchmark_rust.json
109+
110+
python-benchmark:
111+
name: Python baseline
112+
if: (github.event_name == 'workflow_dispatch' && inputs.run_python_backend == true == true) || github.event_name == 'schedule'
113+
runs-on: ubuntu-latest
114+
115+
steps:
116+
- name: Checkout
117+
uses: actions/checkout@v6
118+
119+
- name: Set up Python
120+
uses: actions/setup-python@v6
121+
with:
122+
python-version: "3.13"
123+
124+
- name: Install uv
125+
uses: astral-sh/uv-action@v3
126+
with:
127+
version: "latest"
128+
python-version: "3.13"
129+
130+
- name: Sync dependencies
131+
run: |
132+
set -euo pipefail
133+
uv venv .venv --python 3.13 --allow-existing
134+
uv sync --frozen --extra dev
135+
136+
- name: Run benchmark (Python backend)
137+
env:
138+
GSPPY_BACKEND: python
139+
run: |
140+
set -euo pipefail
141+
uv run python benchmarks/bench_support.py \
142+
--n_tx 5000 \
143+
--tx_len 6 \
144+
--vocab 200 \
145+
--min_support 0.2 \
146+
--warmup \
147+
| tee benchmark_python.log
148+
149+
- name: Extract metrics
150+
id: metrics_python
151+
run: |
152+
set -euo pipefail
153+
python - <<'PY'
154+
import json
155+
import pathlib
156+
import re
157+
import time
158+
159+
log = pathlib.Path("benchmark_python.log").read_text()
160+
py_match = re.search(r"Python:\s*([0-9.]+)s", log)
161+
162+
data = {
163+
"timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
164+
"backend": "python",
165+
"n_tx": 5000,
166+
"tx_len": 6,
167+
"vocab": 200,
168+
"min_support": 0.2,
169+
}
170+
171+
if py_match:
172+
data["python_time_s"] = float(py_match.group(1))
173+
174+
pathlib.Path("benchmark_python.json").write_text(json.dumps(data, indent=2))
175+
PY
176+
177+
- name: Upload benchmark artifacts
178+
uses: actions/upload-artifact@v6
179+
with:
180+
name: python-benchmark
181+
path: |
182+
benchmark_python.log
183+
benchmark_python.json

.github/workflows/code_quality.yml

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,48 +6,78 @@ on:
66

77
jobs:
88
code-quality:
9-
name: Code Quality Checks
9+
name: Code Quality (Python ${{ matrix.python-version }})
1010
runs-on: ubuntu-latest
11+
strategy:
12+
fail-fast: false
13+
matrix:
14+
python-version: [ "3.10", "3.11", "3.12", "3.13" ]
1115

1216
steps:
13-
# Step 1: Checkout the repository code
1417
- name: Checkout code
1518
uses: actions/checkout@v6
1619

17-
# Step 2: Install uv
18-
- name: Install uv
19-
run: |
20-
curl -Ls https://astral.sh/uv/install.sh | bash
21-
echo "${HOME}/.local/bin" >> $GITHUB_PATH
20+
- name: Set up uv
21+
uses: astral-sh/setup-uv@v7
22+
with:
23+
python-version: ${{ matrix.python-version }}
24+
enable-cache: true
2225

23-
# Step 3: Set up environment and install dependencies
2426
- name: Install dependencies
2527
run: |
26-
uv venv .venv
2728
uv sync --frozen --extra dev
28-
uv pip install -e .
2929
30-
# Step 4: Get changed Python files
31-
- name: Get Python changed files
32-
id: changed-py-files
33-
uses: tj-actions/changed-files@v47
30+
- name: Check lockfile freshness
31+
run: uv lock --check
32+
33+
- name: Run tests with coverage
34+
run: |
35+
uv run pytest --cov=gsppy --cov-branch --cov-report=term-missing:skip-covered --cov-report=xml
36+
37+
- name: Run mypy
38+
run: uv run mypy .
39+
40+
- name: Run pyright
41+
run: uv run pyright
42+
43+
- name: Upload coverage report
44+
if: always()
45+
uses: actions/upload-artifact@v6
46+
with:
47+
name: coverage-${{ matrix.python-version }}
48+
path: coverage.xml
49+
if-no-files-found: ignore
50+
51+
rust-backend:
52+
name: Rust Backend (Python 3.12)
53+
runs-on: ubuntu-latest
54+
steps:
55+
- name: Checkout code
56+
uses: actions/checkout@v6
57+
58+
- name: Set up uv
59+
uses: astral-sh/setup-uv@v7
3460
with:
35-
files: |
36-
*.py
37-
**/*.py
61+
python-version: "3.12"
62+
enable-cache: true
3863

39-
# Step 5: Run Ruff for only changed files
40-
- name: Run Ruff (Lint)
41-
if: steps.changed-py-files.outputs.any_changed == 'true'
64+
- name: Install Rust toolchain
65+
uses: dtolnay/rust-toolchain@stable
66+
67+
- name: Install dependencies
4268
run: |
43-
echo "Running Ruff on changed files..."
44-
echo "Changed files: ${{ steps.changed-py-files.outputs.all_changed_files }}"
45-
uv run ruff check ${{ steps.changed-py-files.outputs.all_changed_files }}
69+
uv sync --frozen --extra dev
4670
47-
# Step 6: Run Pyright for only changed files
48-
- name: Run Pyright (Type Check)
49-
if: steps.changed-py-files.outputs.any_changed == 'true'
71+
- name: Build Rust extension
5072
run: |
51-
echo "Running Pyright on changed files..."
52-
echo "Changed files: ${{ steps.changed-py-files.outputs.all_changed_files }}"
53-
uv run pyright ${{ steps.changed-py-files.outputs.all_changed_files }}
73+
# Ensure a cargo env file exists so that Makefile's `source "$HOME/.cargo/env"` does not fail
74+
if [ ! -f "$HOME/.cargo/env" ]; then
75+
mkdir -p "$HOME/.cargo"
76+
printf '# no-op cargo env for CI; Rust is already available in PATH\n' > "$HOME/.cargo/env"
77+
fi
78+
make rust-build
79+
80+
- name: Run tests (Rust backend)
81+
env:
82+
GSPPY_BACKEND: rust
83+
run: uv run pytest

.github/workflows/docs.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: Documentation
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened, edited, ready_for_review]
6+
push:
7+
branches:
8+
- master
9+
release:
10+
types: [created]
11+
workflow_dispatch:
12+
13+
jobs:
14+
build-docs:
15+
name: Build documentation
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Checkout code
19+
uses: actions/checkout@v6
20+
21+
- name: Install uv
22+
run: |
23+
curl -Ls https://astral.sh/uv/install.sh | bash
24+
echo "${HOME}/.local/bin" >> $GITHUB_PATH
25+
26+
- name: Install dependencies
27+
run: |
28+
uv venv .venv
29+
uv sync --extra docs
30+
uv pip install -e .
31+
32+
- name: Build docs
33+
run: uv run mkdocs build --strict
34+
35+
- name: Upload Pages artifact
36+
if: github.event_name == 'release' || (github.event_name == 'push' && github.ref == 'refs/heads/master') || github.event_name == 'workflow_dispatch'
37+
uses: actions/upload-pages-artifact@v4
38+
with:
39+
path: site
40+
41+
deploy-docs:
42+
if: github.event_name == 'release' || (github.event_name == 'push' && github.ref == 'refs/heads/master') || github.event_name == 'workflow_dispatch'
43+
name: Deploy to GitHub Pages
44+
needs: build-docs
45+
runs-on: ubuntu-latest
46+
permissions:
47+
pages: write
48+
id-token: write
49+
environment:
50+
name: github-pages
51+
url: ${{ steps.deployment.outputs.page_url }}
52+
steps:
53+
- name: Deploy
54+
id: deployment
55+
uses: actions/deploy-pages@v4

.github/workflows/publish.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ jobs:
1212
name: pypi
1313
url: https://pypi.org/project/gsppy/
1414
permissions:
15+
contents: write
1516
id-token: write
1617
steps:
1718
- uses: actions/checkout@v6
@@ -30,5 +31,31 @@ jobs:
3031
run: |
3132
python -m build
3233
34+
- name: Install Syft
35+
uses: anchore/sbom-action/download-syft@v0.17.0
36+
37+
- name: Generate SBOM (CycloneDX)
38+
run: syft packages dist -o cyclonedx-json=dist/sbom.json
39+
40+
- name: Install sigstore
41+
run: python -m pip install sigstore==3.5.1
42+
43+
- name: Sign distributions
44+
run: |
45+
cd dist
46+
sigstore sign *.whl *.tar.gz
47+
48+
- name: Upload release assets
49+
uses: softprops/action-gh-release@v2
50+
with:
51+
files: |
52+
dist/*.whl
53+
dist/*.tar.gz
54+
dist/*.whl.sig
55+
dist/*.tar.gz.sig
56+
dist/*.whl.pem
57+
dist/*.tar.gz.pem
58+
dist/sbom.json
59+
3360
- name: Publish package distributions to PyPI
3461
uses: pypa/gh-action-pypi-publish@v1.13.0

0 commit comments

Comments
 (0)