vepyr 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vepyr-0.1.1/.gitattributes +2 -0
- vepyr-0.1.1/.github/workflows/ci.yml +126 -0
- vepyr-0.1.1/.github/workflows/claude-code-review.yml +49 -0
- vepyr-0.1.1/.github/workflows/claude.yml +50 -0
- vepyr-0.1.1/.github/workflows/publish_documentation.yml +33 -0
- vepyr-0.1.1/.github/workflows/publish_to_pypi.yml +220 -0
- vepyr-0.1.1/.github/workflows/version-bump.yml +134 -0
- vepyr-0.1.1/.gitignore +224 -0
- vepyr-0.1.1/.pre-commit-config.yaml +25 -0
- vepyr-0.1.1/.python-version +1 -0
- vepyr-0.1.1/CLAUDE.md +268 -0
- vepyr-0.1.1/CONTRIBUTING.md +47 -0
- vepyr-0.1.1/Cargo.lock +6245 -0
- vepyr-0.1.1/Cargo.toml +49 -0
- vepyr-0.1.1/LICENSE +201 -0
- vepyr-0.1.1/PKG-INFO +313 -0
- vepyr-0.1.1/README.md +288 -0
- vepyr-0.1.1/ROADMAP.md +283 -0
- vepyr-0.1.1/docs/api.md +11 -0
- vepyr-0.1.1/docs/architecture.md +111 -0
- vepyr-0.1.1/docs/assets/logo.png +0 -0
- vepyr-0.1.1/docs/contact.md +4 -0
- vepyr-0.1.1/docs/developers.md +68 -0
- vepyr-0.1.1/docs/index.md +62 -0
- vepyr-0.1.1/docs/logo.png +0 -0
- vepyr-0.1.1/docs/performance.md +86 -0
- vepyr-0.1.1/docs/plugins.md +16 -0
- vepyr-0.1.1/docs/quickstart.md +133 -0
- vepyr-0.1.1/docs/superpowers/plans/2026-05-19-refseq-cache-source-mode.md +961 -0
- vepyr-0.1.1/docs/superpowers/plans/2026-07-05-release-please-pypi-pipeline.md +457 -0
- vepyr-0.1.1/docs/superpowers/specs/2026-07-05-release-please-pypi-pipeline-design.md +208 -0
- vepyr-0.1.1/docs/vepyr_logo_vector.svg +151 -0
- vepyr-0.1.1/e2e-testing/README.md +237 -0
- vepyr-0.1.1/e2e-testing/reports/benchmark_report.json +2364 -0
- vepyr-0.1.1/e2e-testing/reports/benchmark_report.md +501 -0
- vepyr-0.1.1/e2e-testing/reports/issue88_remaining_unresolved.json +96 -0
- vepyr-0.1.1/e2e-testing/reports/issue88_remaining_unresolved.md +41 -0
- vepyr-0.1.1/e2e-testing/reports/mismatches_fjall_vs_vep.tsv +2853 -0
- vepyr-0.1.1/e2e-testing/reports/mismatches_fjall_vs_vep_classified.tsv +2853 -0
- vepyr-0.1.1/e2e-testing/reports/mismatches_parquet_vs_vep.tsv +2865 -0
- vepyr-0.1.1/e2e-testing/reports/mismatches_parquet_vs_vep_classified.tsv +2865 -0
- vepyr-0.1.1/e2e-testing/scripts/run_annotation_fast.py +809 -0
- vepyr-0.1.1/e2e-testing/scripts/run_annotation_fast_all.py +780 -0
- vepyr-0.1.1/e2e-testing/vep-docker.md +286 -0
- vepyr-0.1.1/mkdocs.yml +86 -0
- vepyr-0.1.1/notebooks/annotate.ipynb +520 -0
- vepyr-0.1.1/notebooks/build_cache.ipynb +190 -0
- vepyr-0.1.1/pyproject.toml +45 -0
- vepyr-0.1.1/rust-toolchain.toml +3 -0
- vepyr-0.1.1/src/annotate.rs +422 -0
- vepyr-0.1.1/src/lib.rs +154 -0
- vepyr-0.1.1/src/vepyr/__init__.py +873 -0
- vepyr-0.1.1/src/vepyr/_core.pyi +48 -0
- vepyr-0.1.1/src/vepyr/py.typed +0 -0
- vepyr-0.1.1/tests/__init__.py +1 -0
- vepyr-0.1.1/tests/_golden_suite.py +501 -0
- vepyr-0.1.1/tests/cache_metadata.py +73 -0
- vepyr-0.1.1/tests/data/ensembl_cache/22/16000001-17000000.gz +0 -0
- vepyr-0.1.1/tests/data/ensembl_cache/22/16000001-17000000_reg.gz +0 -0
- vepyr-0.1.1/tests/data/ensembl_cache/22/all_vars.gz +0 -0
- vepyr-0.1.1/tests/data/ensembl_cache/22/all_vars.gz.tbi +0 -0
- vepyr-0.1.1/tests/data/ensembl_cache/info.txt +21 -0
- vepyr-0.1.1/tests/data/ensembl_cache/sample.vcf +8 -0
- vepyr-0.1.1/tests/data/golden/_cache_prep.py +107 -0
- vepyr-0.1.1/tests/data/golden/cache/exon/chr1.parquet +3 -0
- vepyr-0.1.1/tests/data/golden/cache/exon/chrom_manifest.json +7 -0
- vepyr-0.1.1/tests/data/golden/cache/motif/chrom_manifest.json +1 -0
- vepyr-0.1.1/tests/data/golden/cache/regulatory/chr1.parquet +3 -0
- vepyr-0.1.1/tests/data/golden/cache/regulatory/chrom_manifest.json +7 -0
- vepyr-0.1.1/tests/data/golden/cache/transcript/chr1.parquet +3 -0
- vepyr-0.1.1/tests/data/golden/cache/transcript/chrom_manifest.json +7 -0
- vepyr-0.1.1/tests/data/golden/cache/translation_core/chr1.parquet +3 -0
- vepyr-0.1.1/tests/data/golden/cache/translation_core/chrom_manifest.json +7 -0
- vepyr-0.1.1/tests/data/golden/cache/translation_sift/chr1.parquet +3 -0
- vepyr-0.1.1/tests/data/golden/cache/translation_sift/chrom_manifest.json +7 -0
- vepyr-0.1.1/tests/data/golden/cache/variation/chr1.parquet +3 -0
- vepyr-0.1.1/tests/data/golden/cache/variation/chrom_manifest.json +7 -0
- vepyr-0.1.1/tests/data/golden/golden.vcf +338 -0
- vepyr-0.1.1/tests/data/golden/input.vcf +335 -0
- vepyr-0.1.1/tests/data/golden/input.vcf.gz +0 -0
- vepyr-0.1.1/tests/data/golden/input.vcf.gz.tbi +0 -0
- vepyr-0.1.1/tests/data/golden/prepare.py +167 -0
- vepyr-0.1.1/tests/data/golden/reference.fa +14350 -0
- vepyr-0.1.1/tests/data/golden/reference.fa.fai +1 -0
- vepyr-0.1.1/tests/data/golden_merged/cache/exon/chr1.parquet +3 -0
- vepyr-0.1.1/tests/data/golden_merged/cache/exon/chrom_manifest.json +7 -0
- vepyr-0.1.1/tests/data/golden_merged/cache/motif/chrom_manifest.json +1 -0
- vepyr-0.1.1/tests/data/golden_merged/cache/regulatory/chr1.parquet +3 -0
- vepyr-0.1.1/tests/data/golden_merged/cache/regulatory/chrom_manifest.json +7 -0
- vepyr-0.1.1/tests/data/golden_merged/cache/transcript/chr1.parquet +3 -0
- vepyr-0.1.1/tests/data/golden_merged/cache/transcript/chrom_manifest.json +7 -0
- vepyr-0.1.1/tests/data/golden_merged/cache/translation_core/chr1.parquet +3 -0
- vepyr-0.1.1/tests/data/golden_merged/cache/translation_core/chrom_manifest.json +7 -0
- vepyr-0.1.1/tests/data/golden_merged/cache/translation_sift/chr1.parquet +3 -0
- vepyr-0.1.1/tests/data/golden_merged/cache/translation_sift/chrom_manifest.json +7 -0
- vepyr-0.1.1/tests/data/golden_merged/cache/variation/chr1.parquet +3 -0
- vepyr-0.1.1/tests/data/golden_merged/cache/variation/chrom_manifest.json +7 -0
- vepyr-0.1.1/tests/data/golden_merged/golden.vcf +336 -0
- vepyr-0.1.1/tests/data/golden_merged/prepare.py +111 -0
- vepyr-0.1.1/tests/data/golden_merged/prepare_pick_modes.py +105 -0
- vepyr-0.1.1/tests/data/golden_merged_flag_pick_allele/golden.vcf +336 -0
- vepyr-0.1.1/tests/data/golden_merged_per_gene/golden.vcf +336 -0
- vepyr-0.1.1/tests/data/golden_merged_pick_allele/golden.vcf +336 -0
- vepyr-0.1.1/tests/data/golden_merged_pick_allele_gene/golden.vcf +336 -0
- vepyr-0.1.1/tests/test_annotate.py +584 -0
- vepyr-0.1.1/tests/test_build_cache.py +907 -0
- vepyr-0.1.1/tests/test_cache_metadata.py +24 -0
- vepyr-0.1.1/tests/test_golden.py +30 -0
- vepyr-0.1.1/tests/test_golden_merged.py +39 -0
- vepyr-0.1.1/tests/test_golden_merged_flag_pick_allele.py +45 -0
- vepyr-0.1.1/tests/test_golden_merged_per_gene.py +46 -0
- vepyr-0.1.1/tests/test_golden_merged_pick_allele.py +46 -0
- vepyr-0.1.1/tests/test_golden_merged_pick_allele_gene.py +46 -0
- vepyr-0.1.1/tests/test_import.py +17 -0
- vepyr-0.1.1/tests/test_run_annotation_fast.py +405 -0
- vepyr-0.1.1/uv.lock +1299 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [master]
|
|
6
|
+
paths-ignore:
|
|
7
|
+
- "docs/**"
|
|
8
|
+
- "mkdocs.yml"
|
|
9
|
+
- "*.md"
|
|
10
|
+
pull_request:
|
|
11
|
+
branches: [master]
|
|
12
|
+
paths-ignore:
|
|
13
|
+
- "docs/**"
|
|
14
|
+
- "mkdocs.yml"
|
|
15
|
+
- "*.md"
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
19
|
+
cancel-in-progress: true
|
|
20
|
+
|
|
21
|
+
env:
|
|
22
|
+
CARGO_TERM_COLOR: always
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
lint:
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
- uses: dtolnay/rust-toolchain@1.91
|
|
30
|
+
with:
|
|
31
|
+
components: rustfmt, clippy
|
|
32
|
+
- uses: Swatinem/rust-cache@v2
|
|
33
|
+
- run: cargo fmt --check
|
|
34
|
+
- run: cargo clippy --all-targets -- -D warnings
|
|
35
|
+
|
|
36
|
+
test-rust:
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v4
|
|
40
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
41
|
+
- uses: Swatinem/rust-cache@v2
|
|
42
|
+
# The `extension-module` feature makes PyO3 skip linking libpython (the
|
|
43
|
+
# interpreter supplies the symbols when the module is imported), which
|
|
44
|
+
# leaves a standalone `cargo test` binary with undefined Python symbols.
|
|
45
|
+
# Build the tests without it so PyO3 links libpython, and expose that
|
|
46
|
+
# library dir to the runtime linker.
|
|
47
|
+
- name: cargo test
|
|
48
|
+
run: |
|
|
49
|
+
export LD_LIBRARY_PATH="$(python3 -c 'import sysconfig; print(sysconfig.get_config_var("LIBDIR"))'):${LD_LIBRARY_PATH:-}"
|
|
50
|
+
cargo test --no-default-features
|
|
51
|
+
|
|
52
|
+
linux-tests:
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
strategy:
|
|
55
|
+
fail-fast: false
|
|
56
|
+
matrix:
|
|
57
|
+
target: [x86_64]
|
|
58
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v4
|
|
61
|
+
with:
|
|
62
|
+
lfs: true
|
|
63
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
64
|
+
- uses: Swatinem/rust-cache@v2
|
|
65
|
+
- uses: astral-sh/setup-uv@v5
|
|
66
|
+
- name: Build and test
|
|
67
|
+
run: |
|
|
68
|
+
uv python install ${{ matrix.python-version }}
|
|
69
|
+
uv sync --python ${{ matrix.python-version }}
|
|
70
|
+
uv run pytest -v
|
|
71
|
+
|
|
72
|
+
linux:
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
strategy:
|
|
75
|
+
matrix:
|
|
76
|
+
target: [x86_64]
|
|
77
|
+
steps:
|
|
78
|
+
- uses: actions/checkout@v4
|
|
79
|
+
- name: Build wheels
|
|
80
|
+
uses: PyO3/maturin-action@v1
|
|
81
|
+
with:
|
|
82
|
+
target: ${{ matrix.target }}
|
|
83
|
+
args: --release --out dist --find-interpreter
|
|
84
|
+
sccache: true
|
|
85
|
+
manylinux: auto
|
|
86
|
+
- uses: actions/upload-artifact@v4
|
|
87
|
+
with:
|
|
88
|
+
name: wheels-manylinux-${{ matrix.target }}
|
|
89
|
+
path: dist
|
|
90
|
+
|
|
91
|
+
macos:
|
|
92
|
+
runs-on: ${{ matrix.target == 'aarch64' && 'macos-latest' || 'macos-14' }}
|
|
93
|
+
strategy:
|
|
94
|
+
matrix:
|
|
95
|
+
target: [x86_64, aarch64]
|
|
96
|
+
steps:
|
|
97
|
+
- uses: actions/checkout@v4
|
|
98
|
+
- name: Build wheels
|
|
99
|
+
uses: PyO3/maturin-action@v1
|
|
100
|
+
with:
|
|
101
|
+
target: ${{ matrix.target }}
|
|
102
|
+
args: --release --out dist --find-interpreter
|
|
103
|
+
sccache: true
|
|
104
|
+
- uses: actions/upload-artifact@v4
|
|
105
|
+
with:
|
|
106
|
+
name: wheels-macos-${{ matrix.target }}
|
|
107
|
+
path: dist
|
|
108
|
+
|
|
109
|
+
windows:
|
|
110
|
+
runs-on: windows-latest
|
|
111
|
+
strategy:
|
|
112
|
+
matrix:
|
|
113
|
+
target: [x64]
|
|
114
|
+
steps:
|
|
115
|
+
- uses: actions/checkout@v4
|
|
116
|
+
- name: Build wheels
|
|
117
|
+
uses: PyO3/maturin-action@v1
|
|
118
|
+
with:
|
|
119
|
+
target: ${{ matrix.target }}
|
|
120
|
+
maturin-version: v1.13.3
|
|
121
|
+
args: --release --out dist --find-interpreter
|
|
122
|
+
sccache: true
|
|
123
|
+
- uses: actions/upload-artifact@v4
|
|
124
|
+
with:
|
|
125
|
+
name: wheels-win-${{ matrix.target }}
|
|
126
|
+
path: dist
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Claude Code Review
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [opened, synchronize, ready_for_review, reopened]
|
|
6
|
+
# Optional: Only run on specific file changes
|
|
7
|
+
# paths:
|
|
8
|
+
# - "src/**/*.ts"
|
|
9
|
+
# - "src/**/*.tsx"
|
|
10
|
+
# - "src/**/*.js"
|
|
11
|
+
# - "src/**/*.jsx"
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
claude-review:
|
|
15
|
+
# Optional: Filter by PR author
|
|
16
|
+
# if: |
|
|
17
|
+
# github.event.pull_request.user.login == 'external-contributor' ||
|
|
18
|
+
# github.event.pull_request.user.login == 'new-developer' ||
|
|
19
|
+
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
|
|
20
|
+
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
pull-requests: read
|
|
25
|
+
issues: read
|
|
26
|
+
id-token: write
|
|
27
|
+
|
|
28
|
+
steps:
|
|
29
|
+
- name: Checkout repository
|
|
30
|
+
uses: actions/checkout@v4
|
|
31
|
+
with:
|
|
32
|
+
fetch-depth: 1
|
|
33
|
+
|
|
34
|
+
- name: Run Claude Code Review
|
|
35
|
+
id: claude-review
|
|
36
|
+
# Non-blocking: this optional review bot requires the Claude GitHub App
|
|
37
|
+
# to be installed on the repo (https://github.com/apps/claude). Until it
|
|
38
|
+
# is, the OIDC->app-token exchange returns 401; don't fail the PR's
|
|
39
|
+
# checks over it. Once the app is installed, the review runs normally.
|
|
40
|
+
continue-on-error: true
|
|
41
|
+
uses: anthropics/claude-code-action@v1
|
|
42
|
+
with:
|
|
43
|
+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
44
|
+
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
|
|
45
|
+
plugins: 'code-review@claude-code-plugins'
|
|
46
|
+
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
|
|
47
|
+
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
48
|
+
# or https://code.claude.com/docs/en/cli-reference for available options
|
|
49
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: Claude Code
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issue_comment:
|
|
5
|
+
types: [created]
|
|
6
|
+
pull_request_review_comment:
|
|
7
|
+
types: [created]
|
|
8
|
+
issues:
|
|
9
|
+
types: [opened, assigned]
|
|
10
|
+
pull_request_review:
|
|
11
|
+
types: [submitted]
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
claude:
|
|
15
|
+
if: |
|
|
16
|
+
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
17
|
+
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
|
18
|
+
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
|
19
|
+
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
pull-requests: read
|
|
24
|
+
issues: read
|
|
25
|
+
id-token: write
|
|
26
|
+
actions: read # Required for Claude to read CI results on PRs
|
|
27
|
+
steps:
|
|
28
|
+
- name: Checkout repository
|
|
29
|
+
uses: actions/checkout@v4
|
|
30
|
+
with:
|
|
31
|
+
fetch-depth: 1
|
|
32
|
+
|
|
33
|
+
- name: Run Claude Code
|
|
34
|
+
id: claude
|
|
35
|
+
uses: anthropics/claude-code-action@v1
|
|
36
|
+
with:
|
|
37
|
+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
38
|
+
|
|
39
|
+
# This is an optional setting that allows Claude to read CI results on PRs
|
|
40
|
+
additional_permissions: |
|
|
41
|
+
actions: read
|
|
42
|
+
|
|
43
|
+
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
|
|
44
|
+
# prompt: 'Update the pull request description to include a summary of changes.'
|
|
45
|
+
|
|
46
|
+
# Optional: Add claude_args to customize behavior and configuration
|
|
47
|
+
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
|
|
48
|
+
# or https://code.claude.com/docs/en/cli-reference for available options
|
|
49
|
+
# claude_args: '--allowed-tools Bash(gh pr:*)'
|
|
50
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: Publish docs via GitHub Pages
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
name: Deploy docs
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
|
|
23
|
+
- name: Install docs dependencies
|
|
24
|
+
run: |
|
|
25
|
+
pip install mkdocs mkdocs-material mkdocstrings mkdocstrings-python
|
|
26
|
+
|
|
27
|
+
- name: Install vepyr (stub for mkdocstrings)
|
|
28
|
+
run: pip install -e . || echo "Native build skipped — mkdocstrings will use stubs"
|
|
29
|
+
|
|
30
|
+
- name: Deploy docs
|
|
31
|
+
run: mkdocs gh-deploy --force --remote-branch gh-pages -f mkdocs.yml
|
|
32
|
+
env:
|
|
33
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Manual workflow (B of 2): tag the released version on the default branch,
|
|
4
|
+
# build wheels, run the test gate, and publish to PyPI via OIDC Trusted
|
|
5
|
+
# Publishing. Run "Version Bump" first and merge its PR, then run this with the
|
|
6
|
+
# same version.
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
inputs:
|
|
11
|
+
version:
|
|
12
|
+
description: "Version to release (must already be merged to the default branch), e.g. 0.2.0"
|
|
13
|
+
required: true
|
|
14
|
+
type: string
|
|
15
|
+
|
|
16
|
+
permissions: {}
|
|
17
|
+
|
|
18
|
+
env:
|
|
19
|
+
CARGO_TERM_COLOR: always
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
prepare:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
permissions:
|
|
25
|
+
contents: write
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
with:
|
|
29
|
+
fetch-depth: 0
|
|
30
|
+
- name: Ensure the tag exists for this version
|
|
31
|
+
env:
|
|
32
|
+
GH_TOKEN: ${{ github.token }}
|
|
33
|
+
run: |
|
|
34
|
+
set -euo pipefail
|
|
35
|
+
VERSION="${{ inputs.version }}"
|
|
36
|
+
if git rev-parse "refs/tags/${VERSION}" >/dev/null 2>&1; then
|
|
37
|
+
echo "Tag ${VERSION} already exists; using it."
|
|
38
|
+
exit 0
|
|
39
|
+
fi
|
|
40
|
+
FILE_VER=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
|
|
41
|
+
PY_VER=$(grep '^version' pyproject.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
|
|
42
|
+
if [ "$FILE_VER" != "$VERSION" ] || [ "$PY_VER" != "$VERSION" ]; then
|
|
43
|
+
echo "::error::Version mismatch on ${GITHUB_REF_NAME}: Cargo.toml=${FILE_VER} pyproject.toml=${PY_VER} but requested ${VERSION}. Merge the Version Bump PR first."
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
git config user.name "github-actions[bot]"
|
|
47
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
48
|
+
git tag -a "${VERSION}" -m "Release ${VERSION}"
|
|
49
|
+
git push origin "refs/tags/${VERSION}"
|
|
50
|
+
echo "Created and pushed tag ${VERSION}."
|
|
51
|
+
|
|
52
|
+
test-rust:
|
|
53
|
+
needs: prepare
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
permissions:
|
|
56
|
+
contents: read
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
with:
|
|
60
|
+
ref: ${{ inputs.version }}
|
|
61
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
62
|
+
- uses: Swatinem/rust-cache@v2
|
|
63
|
+
# extension-module makes PyO3 skip linking libpython; build tests without
|
|
64
|
+
# it so the standalone test binary links libpython, and expose LIBDIR.
|
|
65
|
+
- name: cargo test
|
|
66
|
+
run: |
|
|
67
|
+
export LD_LIBRARY_PATH="$(python3 -c 'import sysconfig; print(sysconfig.get_config_var("LIBDIR"))'):${LD_LIBRARY_PATH:-}"
|
|
68
|
+
cargo test --no-default-features
|
|
69
|
+
|
|
70
|
+
test-python:
|
|
71
|
+
needs: prepare
|
|
72
|
+
runs-on: ubuntu-latest
|
|
73
|
+
permissions:
|
|
74
|
+
contents: read
|
|
75
|
+
steps:
|
|
76
|
+
- uses: actions/checkout@v4
|
|
77
|
+
with:
|
|
78
|
+
ref: ${{ inputs.version }}
|
|
79
|
+
lfs: true
|
|
80
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
81
|
+
- uses: Swatinem/rust-cache@v2
|
|
82
|
+
- uses: astral-sh/setup-uv@v5
|
|
83
|
+
- name: Build and test
|
|
84
|
+
run: |
|
|
85
|
+
uv python install 3.12
|
|
86
|
+
uv sync --python 3.12
|
|
87
|
+
uv run pytest -v
|
|
88
|
+
|
|
89
|
+
sdist:
|
|
90
|
+
needs: prepare
|
|
91
|
+
runs-on: ubuntu-latest
|
|
92
|
+
permissions:
|
|
93
|
+
contents: read
|
|
94
|
+
steps:
|
|
95
|
+
- uses: actions/checkout@v4
|
|
96
|
+
with:
|
|
97
|
+
ref: ${{ inputs.version }}
|
|
98
|
+
- uses: PyO3/maturin-action@v1
|
|
99
|
+
with:
|
|
100
|
+
command: sdist
|
|
101
|
+
args: --out dist
|
|
102
|
+
- uses: actions/upload-artifact@v4
|
|
103
|
+
with:
|
|
104
|
+
name: wheels-sdist
|
|
105
|
+
path: dist
|
|
106
|
+
|
|
107
|
+
linux:
|
|
108
|
+
needs: prepare
|
|
109
|
+
runs-on: ubuntu-latest
|
|
110
|
+
permissions:
|
|
111
|
+
contents: read
|
|
112
|
+
strategy:
|
|
113
|
+
matrix:
|
|
114
|
+
target: [x86_64]
|
|
115
|
+
steps:
|
|
116
|
+
- uses: actions/checkout@v4
|
|
117
|
+
with:
|
|
118
|
+
ref: ${{ inputs.version }}
|
|
119
|
+
- name: Build wheels
|
|
120
|
+
uses: PyO3/maturin-action@v1
|
|
121
|
+
with:
|
|
122
|
+
target: ${{ matrix.target }}
|
|
123
|
+
args: --release --out dist --find-interpreter
|
|
124
|
+
sccache: true
|
|
125
|
+
manylinux: auto
|
|
126
|
+
- uses: actions/upload-artifact@v4
|
|
127
|
+
with:
|
|
128
|
+
name: wheels-manylinux-${{ matrix.target }}
|
|
129
|
+
path: dist
|
|
130
|
+
|
|
131
|
+
macos:
|
|
132
|
+
needs: prepare
|
|
133
|
+
runs-on: ${{ matrix.target == 'aarch64' && 'macos-latest' || 'macos-14' }}
|
|
134
|
+
permissions:
|
|
135
|
+
contents: read
|
|
136
|
+
strategy:
|
|
137
|
+
matrix:
|
|
138
|
+
target: [x86_64, aarch64]
|
|
139
|
+
steps:
|
|
140
|
+
- uses: actions/checkout@v4
|
|
141
|
+
with:
|
|
142
|
+
ref: ${{ inputs.version }}
|
|
143
|
+
- name: Build wheels
|
|
144
|
+
uses: PyO3/maturin-action@v1
|
|
145
|
+
with:
|
|
146
|
+
target: ${{ matrix.target }}
|
|
147
|
+
args: --release --out dist --find-interpreter
|
|
148
|
+
sccache: true
|
|
149
|
+
- uses: actions/upload-artifact@v4
|
|
150
|
+
with:
|
|
151
|
+
name: wheels-macos-${{ matrix.target }}
|
|
152
|
+
path: dist
|
|
153
|
+
|
|
154
|
+
windows:
|
|
155
|
+
needs: prepare
|
|
156
|
+
runs-on: windows-latest
|
|
157
|
+
permissions:
|
|
158
|
+
contents: read
|
|
159
|
+
strategy:
|
|
160
|
+
matrix:
|
|
161
|
+
target: [x64]
|
|
162
|
+
steps:
|
|
163
|
+
- uses: actions/checkout@v4
|
|
164
|
+
with:
|
|
165
|
+
ref: ${{ inputs.version }}
|
|
166
|
+
- name: Build wheels
|
|
167
|
+
uses: PyO3/maturin-action@v1
|
|
168
|
+
with:
|
|
169
|
+
target: ${{ matrix.target }}
|
|
170
|
+
maturin-version: v1.13.3
|
|
171
|
+
args: --release --out dist --find-interpreter
|
|
172
|
+
sccache: true
|
|
173
|
+
- uses: actions/upload-artifact@v4
|
|
174
|
+
with:
|
|
175
|
+
name: wheels-win-${{ matrix.target }}
|
|
176
|
+
path: dist
|
|
177
|
+
|
|
178
|
+
publish:
|
|
179
|
+
name: Publish to PyPI
|
|
180
|
+
needs: [prepare, test-rust, test-python, sdist, linux, macos, windows]
|
|
181
|
+
runs-on: ubuntu-latest
|
|
182
|
+
environment: pypi
|
|
183
|
+
permissions:
|
|
184
|
+
id-token: write
|
|
185
|
+
steps:
|
|
186
|
+
- uses: actions/download-artifact@v4
|
|
187
|
+
with:
|
|
188
|
+
pattern: wheels-*
|
|
189
|
+
merge-multiple: true
|
|
190
|
+
path: dist
|
|
191
|
+
- name: Publish to PyPI (OIDC Trusted Publishing)
|
|
192
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
193
|
+
with:
|
|
194
|
+
skip-existing: true
|
|
195
|
+
attestations: true
|
|
196
|
+
|
|
197
|
+
github-release:
|
|
198
|
+
needs: [publish]
|
|
199
|
+
runs-on: ubuntu-latest
|
|
200
|
+
permissions:
|
|
201
|
+
contents: write
|
|
202
|
+
steps:
|
|
203
|
+
- uses: actions/checkout@v4
|
|
204
|
+
with:
|
|
205
|
+
ref: ${{ inputs.version }}
|
|
206
|
+
- uses: actions/download-artifact@v4
|
|
207
|
+
with:
|
|
208
|
+
pattern: wheels-*
|
|
209
|
+
merge-multiple: true
|
|
210
|
+
path: dist
|
|
211
|
+
- name: Create or update the GitHub Release
|
|
212
|
+
env:
|
|
213
|
+
GH_TOKEN: ${{ github.token }}
|
|
214
|
+
run: |
|
|
215
|
+
TAG="${{ inputs.version }}"
|
|
216
|
+
if gh release view "$TAG" >/dev/null 2>&1; then
|
|
217
|
+
gh release upload "$TAG" dist/* --clobber
|
|
218
|
+
else
|
|
219
|
+
gh release create "$TAG" dist/* --generate-notes
|
|
220
|
+
fi
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
name: Version Bump
|
|
2
|
+
|
|
3
|
+
# Manual workflow (A of 2): compute the next semantic version from Conventional
|
|
4
|
+
# Commit messages since the last tag, update Cargo.toml + pyproject.toml, and
|
|
5
|
+
# open a pull request with the bump. Merge that PR, then run "Publish to PyPI"
|
|
6
|
+
# with the same version. This never pushes to master directly, so it needs no
|
|
7
|
+
# bypass of branch protection.
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
inputs:
|
|
12
|
+
bump:
|
|
13
|
+
description: "Version bump (auto = derive from commit messages)"
|
|
14
|
+
required: true
|
|
15
|
+
type: choice
|
|
16
|
+
options:
|
|
17
|
+
- auto
|
|
18
|
+
- patch
|
|
19
|
+
- minor
|
|
20
|
+
- major
|
|
21
|
+
default: auto
|
|
22
|
+
dry_run:
|
|
23
|
+
description: "Dry run (compute only; no branch/PR)"
|
|
24
|
+
required: false
|
|
25
|
+
type: boolean
|
|
26
|
+
default: false
|
|
27
|
+
|
|
28
|
+
permissions:
|
|
29
|
+
contents: write
|
|
30
|
+
pull-requests: write
|
|
31
|
+
|
|
32
|
+
jobs:
|
|
33
|
+
bump:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
with:
|
|
38
|
+
fetch-depth: 0
|
|
39
|
+
|
|
40
|
+
- name: Install cargo-set-version
|
|
41
|
+
run: cargo install cargo-set-version --locked
|
|
42
|
+
|
|
43
|
+
- name: Compute next version
|
|
44
|
+
id: compute
|
|
45
|
+
run: |
|
|
46
|
+
set -euo pipefail
|
|
47
|
+
CURRENT=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
|
|
48
|
+
echo "current=$CURRENT" >> "$GITHUB_OUTPUT"
|
|
49
|
+
|
|
50
|
+
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
|
51
|
+
if [ -n "$LAST_TAG" ]; then
|
|
52
|
+
RANGE="${LAST_TAG}..HEAD"
|
|
53
|
+
else
|
|
54
|
+
RANGE="HEAD"
|
|
55
|
+
fi
|
|
56
|
+
echo "Analyzing commits in range: ${RANGE:-<all>} (last tag: ${LAST_TAG:-none})"
|
|
57
|
+
|
|
58
|
+
BUMP="${{ inputs.bump }}"
|
|
59
|
+
if [ "$BUMP" = "auto" ]; then
|
|
60
|
+
LOG=$(git log $RANGE --format='%s%n%b')
|
|
61
|
+
if echo "$LOG" | grep -qE '^[a-zA-Z]+(\([^)]*\))?!:' \
|
|
62
|
+
|| echo "$LOG" | grep -qE '(^|[[:space:]])BREAKING[ -]CHANGE'; then
|
|
63
|
+
BUMP=major
|
|
64
|
+
elif echo "$LOG" | grep -qE '^feat(\([^)]*\))?:'; then
|
|
65
|
+
BUMP=minor
|
|
66
|
+
elif echo "$LOG" | grep -qE '^fix(\([^)]*\))?:'; then
|
|
67
|
+
BUMP=patch
|
|
68
|
+
else
|
|
69
|
+
echo "No feat/fix/breaking commits found; defaulting to patch."
|
|
70
|
+
BUMP=patch
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
echo "bump=$BUMP" >> "$GITHUB_OUTPUT"
|
|
74
|
+
|
|
75
|
+
IFS='.' read -r MAJ MIN PAT <<< "$CURRENT"
|
|
76
|
+
case "$BUMP" in
|
|
77
|
+
major) MAJ=$((MAJ + 1)); MIN=0; PAT=0 ;;
|
|
78
|
+
minor) MIN=$((MIN + 1)); PAT=0 ;;
|
|
79
|
+
patch) PAT=$((PAT + 1)) ;;
|
|
80
|
+
esac
|
|
81
|
+
NEXT="${MAJ}.${MIN}.${PAT}"
|
|
82
|
+
echo "next=$NEXT" >> "$GITHUB_OUTPUT"
|
|
83
|
+
echo "Next version: $CURRENT -> $NEXT ($BUMP)"
|
|
84
|
+
|
|
85
|
+
- name: Guard against existing tag
|
|
86
|
+
run: |
|
|
87
|
+
NEXT="${{ steps.compute.outputs.next }}"
|
|
88
|
+
if git rev-parse "refs/tags/${NEXT}" >/dev/null 2>&1; then
|
|
89
|
+
echo "::error::Tag ${NEXT} already exists. Aborting."
|
|
90
|
+
exit 1
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
- name: Update version in files
|
|
94
|
+
run: |
|
|
95
|
+
NEXT="${{ steps.compute.outputs.next }}"
|
|
96
|
+
cargo set-version "$NEXT"
|
|
97
|
+
sed -i "s/^version = \".*\"/version = \"$NEXT\"/" pyproject.toml
|
|
98
|
+
|
|
99
|
+
- name: Open version-bump pull request
|
|
100
|
+
if: ${{ inputs.dry_run == false }}
|
|
101
|
+
env:
|
|
102
|
+
GH_TOKEN: ${{ github.token }}
|
|
103
|
+
run: |
|
|
104
|
+
set -euo pipefail
|
|
105
|
+
NEXT="${{ steps.compute.outputs.next }}"
|
|
106
|
+
BRANCH="release/${NEXT}"
|
|
107
|
+
BASE="${GITHUB_REF_NAME}"
|
|
108
|
+
git config user.name "github-actions[bot]"
|
|
109
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
110
|
+
git checkout -b "$BRANCH"
|
|
111
|
+
git add Cargo.toml pyproject.toml
|
|
112
|
+
git commit -m "chore(release): ${NEXT}"
|
|
113
|
+
git push -u origin "$BRANCH" --force-with-lease
|
|
114
|
+
gh pr create \
|
|
115
|
+
--base "$BASE" \
|
|
116
|
+
--head "$BRANCH" \
|
|
117
|
+
--title "chore(release): ${NEXT}" \
|
|
118
|
+
--body "Automated version bump \`${{ steps.compute.outputs.current }}\` → \`${NEXT}\` (${{ steps.compute.outputs.bump }}).
|
|
119
|
+
|
|
120
|
+
After merging, run the **Publish to PyPI** workflow with version \`${NEXT}\` to tag, build, and publish."
|
|
121
|
+
|
|
122
|
+
- name: Summary
|
|
123
|
+
run: |
|
|
124
|
+
{
|
|
125
|
+
echo "### Version Bump"
|
|
126
|
+
echo "- **From:** ${{ steps.compute.outputs.current }}"
|
|
127
|
+
echo "- **Bump:** ${{ steps.compute.outputs.bump }}"
|
|
128
|
+
echo "- **To:** ${{ steps.compute.outputs.next }}"
|
|
129
|
+
echo "- **Dry run:** ${{ inputs.dry_run }}"
|
|
130
|
+
if [ "${{ inputs.dry_run }}" = "false" ]; then
|
|
131
|
+
echo ""
|
|
132
|
+
echo "Opened PR for branch \`release/${{ steps.compute.outputs.next }}\`. Merge it, then run **Publish to PyPI** with version \`${{ steps.compute.outputs.next }}\`."
|
|
133
|
+
fi
|
|
134
|
+
} >> "$GITHUB_STEP_SUMMARY"
|