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.
Files changed (116) hide show
  1. vepyr-0.1.1/.gitattributes +2 -0
  2. vepyr-0.1.1/.github/workflows/ci.yml +126 -0
  3. vepyr-0.1.1/.github/workflows/claude-code-review.yml +49 -0
  4. vepyr-0.1.1/.github/workflows/claude.yml +50 -0
  5. vepyr-0.1.1/.github/workflows/publish_documentation.yml +33 -0
  6. vepyr-0.1.1/.github/workflows/publish_to_pypi.yml +220 -0
  7. vepyr-0.1.1/.github/workflows/version-bump.yml +134 -0
  8. vepyr-0.1.1/.gitignore +224 -0
  9. vepyr-0.1.1/.pre-commit-config.yaml +25 -0
  10. vepyr-0.1.1/.python-version +1 -0
  11. vepyr-0.1.1/CLAUDE.md +268 -0
  12. vepyr-0.1.1/CONTRIBUTING.md +47 -0
  13. vepyr-0.1.1/Cargo.lock +6245 -0
  14. vepyr-0.1.1/Cargo.toml +49 -0
  15. vepyr-0.1.1/LICENSE +201 -0
  16. vepyr-0.1.1/PKG-INFO +313 -0
  17. vepyr-0.1.1/README.md +288 -0
  18. vepyr-0.1.1/ROADMAP.md +283 -0
  19. vepyr-0.1.1/docs/api.md +11 -0
  20. vepyr-0.1.1/docs/architecture.md +111 -0
  21. vepyr-0.1.1/docs/assets/logo.png +0 -0
  22. vepyr-0.1.1/docs/contact.md +4 -0
  23. vepyr-0.1.1/docs/developers.md +68 -0
  24. vepyr-0.1.1/docs/index.md +62 -0
  25. vepyr-0.1.1/docs/logo.png +0 -0
  26. vepyr-0.1.1/docs/performance.md +86 -0
  27. vepyr-0.1.1/docs/plugins.md +16 -0
  28. vepyr-0.1.1/docs/quickstart.md +133 -0
  29. vepyr-0.1.1/docs/superpowers/plans/2026-05-19-refseq-cache-source-mode.md +961 -0
  30. vepyr-0.1.1/docs/superpowers/plans/2026-07-05-release-please-pypi-pipeline.md +457 -0
  31. vepyr-0.1.1/docs/superpowers/specs/2026-07-05-release-please-pypi-pipeline-design.md +208 -0
  32. vepyr-0.1.1/docs/vepyr_logo_vector.svg +151 -0
  33. vepyr-0.1.1/e2e-testing/README.md +237 -0
  34. vepyr-0.1.1/e2e-testing/reports/benchmark_report.json +2364 -0
  35. vepyr-0.1.1/e2e-testing/reports/benchmark_report.md +501 -0
  36. vepyr-0.1.1/e2e-testing/reports/issue88_remaining_unresolved.json +96 -0
  37. vepyr-0.1.1/e2e-testing/reports/issue88_remaining_unresolved.md +41 -0
  38. vepyr-0.1.1/e2e-testing/reports/mismatches_fjall_vs_vep.tsv +2853 -0
  39. vepyr-0.1.1/e2e-testing/reports/mismatches_fjall_vs_vep_classified.tsv +2853 -0
  40. vepyr-0.1.1/e2e-testing/reports/mismatches_parquet_vs_vep.tsv +2865 -0
  41. vepyr-0.1.1/e2e-testing/reports/mismatches_parquet_vs_vep_classified.tsv +2865 -0
  42. vepyr-0.1.1/e2e-testing/scripts/run_annotation_fast.py +809 -0
  43. vepyr-0.1.1/e2e-testing/scripts/run_annotation_fast_all.py +780 -0
  44. vepyr-0.1.1/e2e-testing/vep-docker.md +286 -0
  45. vepyr-0.1.1/mkdocs.yml +86 -0
  46. vepyr-0.1.1/notebooks/annotate.ipynb +520 -0
  47. vepyr-0.1.1/notebooks/build_cache.ipynb +190 -0
  48. vepyr-0.1.1/pyproject.toml +45 -0
  49. vepyr-0.1.1/rust-toolchain.toml +3 -0
  50. vepyr-0.1.1/src/annotate.rs +422 -0
  51. vepyr-0.1.1/src/lib.rs +154 -0
  52. vepyr-0.1.1/src/vepyr/__init__.py +873 -0
  53. vepyr-0.1.1/src/vepyr/_core.pyi +48 -0
  54. vepyr-0.1.1/src/vepyr/py.typed +0 -0
  55. vepyr-0.1.1/tests/__init__.py +1 -0
  56. vepyr-0.1.1/tests/_golden_suite.py +501 -0
  57. vepyr-0.1.1/tests/cache_metadata.py +73 -0
  58. vepyr-0.1.1/tests/data/ensembl_cache/22/16000001-17000000.gz +0 -0
  59. vepyr-0.1.1/tests/data/ensembl_cache/22/16000001-17000000_reg.gz +0 -0
  60. vepyr-0.1.1/tests/data/ensembl_cache/22/all_vars.gz +0 -0
  61. vepyr-0.1.1/tests/data/ensembl_cache/22/all_vars.gz.tbi +0 -0
  62. vepyr-0.1.1/tests/data/ensembl_cache/info.txt +21 -0
  63. vepyr-0.1.1/tests/data/ensembl_cache/sample.vcf +8 -0
  64. vepyr-0.1.1/tests/data/golden/_cache_prep.py +107 -0
  65. vepyr-0.1.1/tests/data/golden/cache/exon/chr1.parquet +3 -0
  66. vepyr-0.1.1/tests/data/golden/cache/exon/chrom_manifest.json +7 -0
  67. vepyr-0.1.1/tests/data/golden/cache/motif/chrom_manifest.json +1 -0
  68. vepyr-0.1.1/tests/data/golden/cache/regulatory/chr1.parquet +3 -0
  69. vepyr-0.1.1/tests/data/golden/cache/regulatory/chrom_manifest.json +7 -0
  70. vepyr-0.1.1/tests/data/golden/cache/transcript/chr1.parquet +3 -0
  71. vepyr-0.1.1/tests/data/golden/cache/transcript/chrom_manifest.json +7 -0
  72. vepyr-0.1.1/tests/data/golden/cache/translation_core/chr1.parquet +3 -0
  73. vepyr-0.1.1/tests/data/golden/cache/translation_core/chrom_manifest.json +7 -0
  74. vepyr-0.1.1/tests/data/golden/cache/translation_sift/chr1.parquet +3 -0
  75. vepyr-0.1.1/tests/data/golden/cache/translation_sift/chrom_manifest.json +7 -0
  76. vepyr-0.1.1/tests/data/golden/cache/variation/chr1.parquet +3 -0
  77. vepyr-0.1.1/tests/data/golden/cache/variation/chrom_manifest.json +7 -0
  78. vepyr-0.1.1/tests/data/golden/golden.vcf +338 -0
  79. vepyr-0.1.1/tests/data/golden/input.vcf +335 -0
  80. vepyr-0.1.1/tests/data/golden/input.vcf.gz +0 -0
  81. vepyr-0.1.1/tests/data/golden/input.vcf.gz.tbi +0 -0
  82. vepyr-0.1.1/tests/data/golden/prepare.py +167 -0
  83. vepyr-0.1.1/tests/data/golden/reference.fa +14350 -0
  84. vepyr-0.1.1/tests/data/golden/reference.fa.fai +1 -0
  85. vepyr-0.1.1/tests/data/golden_merged/cache/exon/chr1.parquet +3 -0
  86. vepyr-0.1.1/tests/data/golden_merged/cache/exon/chrom_manifest.json +7 -0
  87. vepyr-0.1.1/tests/data/golden_merged/cache/motif/chrom_manifest.json +1 -0
  88. vepyr-0.1.1/tests/data/golden_merged/cache/regulatory/chr1.parquet +3 -0
  89. vepyr-0.1.1/tests/data/golden_merged/cache/regulatory/chrom_manifest.json +7 -0
  90. vepyr-0.1.1/tests/data/golden_merged/cache/transcript/chr1.parquet +3 -0
  91. vepyr-0.1.1/tests/data/golden_merged/cache/transcript/chrom_manifest.json +7 -0
  92. vepyr-0.1.1/tests/data/golden_merged/cache/translation_core/chr1.parquet +3 -0
  93. vepyr-0.1.1/tests/data/golden_merged/cache/translation_core/chrom_manifest.json +7 -0
  94. vepyr-0.1.1/tests/data/golden_merged/cache/translation_sift/chr1.parquet +3 -0
  95. vepyr-0.1.1/tests/data/golden_merged/cache/translation_sift/chrom_manifest.json +7 -0
  96. vepyr-0.1.1/tests/data/golden_merged/cache/variation/chr1.parquet +3 -0
  97. vepyr-0.1.1/tests/data/golden_merged/cache/variation/chrom_manifest.json +7 -0
  98. vepyr-0.1.1/tests/data/golden_merged/golden.vcf +336 -0
  99. vepyr-0.1.1/tests/data/golden_merged/prepare.py +111 -0
  100. vepyr-0.1.1/tests/data/golden_merged/prepare_pick_modes.py +105 -0
  101. vepyr-0.1.1/tests/data/golden_merged_flag_pick_allele/golden.vcf +336 -0
  102. vepyr-0.1.1/tests/data/golden_merged_per_gene/golden.vcf +336 -0
  103. vepyr-0.1.1/tests/data/golden_merged_pick_allele/golden.vcf +336 -0
  104. vepyr-0.1.1/tests/data/golden_merged_pick_allele_gene/golden.vcf +336 -0
  105. vepyr-0.1.1/tests/test_annotate.py +584 -0
  106. vepyr-0.1.1/tests/test_build_cache.py +907 -0
  107. vepyr-0.1.1/tests/test_cache_metadata.py +24 -0
  108. vepyr-0.1.1/tests/test_golden.py +30 -0
  109. vepyr-0.1.1/tests/test_golden_merged.py +39 -0
  110. vepyr-0.1.1/tests/test_golden_merged_flag_pick_allele.py +45 -0
  111. vepyr-0.1.1/tests/test_golden_merged_per_gene.py +46 -0
  112. vepyr-0.1.1/tests/test_golden_merged_pick_allele.py +46 -0
  113. vepyr-0.1.1/tests/test_golden_merged_pick_allele_gene.py +46 -0
  114. vepyr-0.1.1/tests/test_import.py +17 -0
  115. vepyr-0.1.1/tests/test_run_annotation_fast.py +405 -0
  116. vepyr-0.1.1/uv.lock +1299 -0
@@ -0,0 +1,2 @@
1
+ tests/data/golden/cache/**/*.parquet filter=lfs diff=lfs merge=lfs -text
2
+ tests/data/golden_merged/cache/**/*.parquet filter=lfs diff=lfs merge=lfs -text
@@ -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"