Skip to content

Commit 3f74e85

Browse files
authored
ci!: move to semantic versioning release mechanism (#5089)
This PR moves the release mechanism to follow the semantic versioning standard with a streamlined RC voting process and automated breaking change detection. See `release_process.md` for the updated workflow and examples.
1 parent 70f6f33 commit 3f74e85

19 files changed

+1831
-775
lines changed

.bumpversion.toml

Lines changed: 12 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[tool.bumpversion]
2-
current_version = "0.32.1"
3-
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(-(?P<prerelease>.+))?"
2+
current_version = "0.40.0-beta.1"
3+
parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(-(?P<prerelease>(beta|rc))\\.(?P<prerelease_num>\\d+))?"
44
serialize = [
5-
"{major}.{minor}.{patch}-{prerelease}",
5+
"{major}.{minor}.{patch}-{prerelease}.{prerelease_num}",
66
"{major}.{minor}.{patch}"
77
]
88
search = "{current_version}"
@@ -18,6 +18,13 @@ allow_dirty = false
1818
commit = false
1919
message = "chore: bump version {current_version} → {new_version}"
2020

21+
[tool.bumpversion.parts.prerelease]
22+
optional_value = "stable"
23+
values = ["beta", "rc", "stable"]
24+
25+
[tool.bumpversion.parts.prerelease_num]
26+
first_value = "0"
27+
2128
[[tool.bumpversion.files]]
2229
filename = "Cargo.toml"
2330
search = 'version = "{current_version}"'
@@ -108,96 +115,8 @@ filename = "Cargo.toml"
108115
search = 'lance-bitpacking = {{ version = "={current_version}"'
109116
replace = 'lance-bitpacking = {{ version = "={new_version}"'
110117

111-
# Update all rust crate Cargo.toml files
112-
[[tool.bumpversion.files]]
113-
filename = "rust/lance/Cargo.toml"
114-
search = 'version = "{current_version}"'
115-
replace = 'version = "{new_version}"'
116-
117-
[[tool.bumpversion.files]]
118-
filename = "rust/lance-arrow/Cargo.toml"
119-
search = 'version = "{current_version}"'
120-
replace = 'version = "{new_version}"'
121-
122-
[[tool.bumpversion.files]]
123-
filename = "rust/lance-core/Cargo.toml"
124-
search = 'version = "{current_version}"'
125-
replace = 'version = "{new_version}"'
126-
127-
[[tool.bumpversion.files]]
128-
filename = "rust/lance-datafusion/Cargo.toml"
129-
search = 'version = "{current_version}"'
130-
replace = 'version = "{new_version}"'
131-
132-
[[tool.bumpversion.files]]
133-
filename = "rust/lance-datagen/Cargo.toml"
134-
search = 'version = "{current_version}"'
135-
replace = 'version = "{new_version}"'
136-
137-
[[tool.bumpversion.files]]
138-
filename = "rust/lance-encoding/Cargo.toml"
139-
search = 'version = "{current_version}"'
140-
replace = 'version = "{new_version}"'
141-
142-
[[tool.bumpversion.files]]
143-
filename = "rust/lance-file/Cargo.toml"
144-
search = 'version = "{current_version}"'
145-
replace = 'version = "{new_version}"'
146-
147-
[[tool.bumpversion.files]]
148-
filename = "rust/lance-index/Cargo.toml"
149-
search = 'version = "{current_version}"'
150-
replace = 'version = "{new_version}"'
151-
152-
[[tool.bumpversion.files]]
153-
filename = "rust/lance-io/Cargo.toml"
154-
search = 'version = "{current_version}"'
155-
replace = 'version = "{new_version}"'
156-
157-
[[tool.bumpversion.files]]
158-
filename = "rust/lance-linalg/Cargo.toml"
159-
search = 'version = "{current_version}"'
160-
replace = 'version = "{new_version}"'
161-
162-
[[tool.bumpversion.files]]
163-
filename = "rust/lance-namespace/Cargo.toml"
164-
search = 'version = "{current_version}"'
165-
replace = 'version = "{new_version}"'
166-
167-
[[tool.bumpversion.files]]
168-
filename = "rust/lance-namespace-impls/Cargo.toml"
169-
search = 'version = "{current_version}"'
170-
replace = 'version = "{new_version}"'
171-
172-
[[tool.bumpversion.files]]
173-
filename = "rust/lance-table/Cargo.toml"
174-
search = 'version = "{current_version}"'
175-
replace = 'version = "{new_version}"'
176-
177-
[[tool.bumpversion.files]]
178-
filename = "rust/compression/bitpacking/Cargo.toml"
179-
search = 'version = "{current_version}"'
180-
replace = 'version = "{new_version}"'
181-
182-
[[tool.bumpversion.files]]
183-
filename = "rust/compression/fsst/Cargo.toml"
184-
search = 'version = "{current_version}"'
185-
replace = 'version = "{new_version}"'
186-
187-
[[tool.bumpversion.files]]
188-
filename = "rust/lance-test-macros/Cargo.toml"
189-
search = 'version = "{current_version}"'
190-
replace = 'version = "{new_version}"'
191-
192-
[[tool.bumpversion.files]]
193-
filename = "rust/lance-testing/Cargo.toml"
194-
search = 'version = "{current_version}"'
195-
replace = 'version = "{new_version}"'
196-
197-
[[tool.bumpversion.files]]
198-
filename = "rust/examples/Cargo.toml"
199-
search = 'version = "{current_version}"'
200-
replace = 'version = "{new_version}"'
118+
# Note: Individual rust crate Cargo.toml files use workspace = true,
119+
# so we don't need to update them individually
201120

202121
# Python Cargo.toml
203122
[[tool.bumpversion.files]]
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: 'Setup Release Environment'
2+
description: 'Sets up Python, Rust, and dependencies for release workflows (assumes repo is already checked out)'
3+
runs:
4+
using: "composite"
5+
steps:
6+
- name: Set up Python
7+
uses: actions/setup-python@v4
8+
with:
9+
python-version: "3.11"
10+
11+
- name: Install dependencies
12+
shell: bash
13+
run: |
14+
pip install bump-my-version packaging PyGithub
15+
16+
- name: Set up Rust
17+
uses: actions-rs/toolchain@v1
18+
with:
19+
toolchain: stable
20+
override: true
21+
22+
- name: Configure git identity
23+
shell: bash
24+
run: |
25+
git config user.name 'Lance Release Bot'
26+
git config user.email '[email protected]'

.github/workflows/approve-rc.yml

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
name: Approve RC
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
rc_tag:
7+
description: 'RC tag to approve (e.g., v1.3.0-rc.2 or v1.3.1-rc.1)'
8+
required: true
9+
type: string
10+
dry_run:
11+
description: 'Dry run (simulate without pushing)'
12+
required: true
13+
default: false
14+
type: boolean
15+
16+
jobs:
17+
approve-rc:
18+
runs-on: ubuntu-latest
19+
outputs:
20+
stable_version: ${{ steps.approve.outputs.STABLE_VERSION }}
21+
stable_tag: ${{ steps.approve.outputs.STABLE_TAG }}
22+
release_branch: ${{ steps.approve.outputs.RELEASE_BRANCH }}
23+
is_major_minor: ${{ steps.approve.outputs.IS_MAJOR_MINOR }}
24+
previous_tag: ${{ steps.approve.outputs.PREVIOUS_TAG }}
25+
steps:
26+
- name: Output Inputs
27+
run: echo "${{ toJSON(github.event.inputs) }}"
28+
29+
- name: Check out repository
30+
uses: actions/checkout@v4
31+
with:
32+
token: ${{ secrets.LANCE_RELEASE_TOKEN }}
33+
fetch-depth: 0
34+
lfs: true
35+
36+
- name: Setup release environment
37+
uses: ./.github/actions/setup-release-env
38+
39+
- name: Approve RC
40+
id: approve
41+
run: |
42+
bash ci/approve_rc.sh "${{ inputs.rc_tag }}"
43+
44+
- name: Push changes (if not dry run)
45+
if: ${{ !inputs.dry_run }}
46+
run: |
47+
git push origin "${{ steps.approve.outputs.RELEASE_BRANCH }}"
48+
git push origin "${{ steps.approve.outputs.STABLE_TAG }}"
49+
50+
- name: Generate Release Notes (if not dry run)
51+
if: ${{ !inputs.dry_run }}
52+
id: release_notes
53+
env:
54+
GH_TOKEN: ${{ secrets.LANCE_RELEASE_TOKEN }}
55+
run: |
56+
PREVIOUS_TAG="${{ steps.approve.outputs.PREVIOUS_TAG }}"
57+
STABLE_TAG="${{ steps.approve.outputs.STABLE_TAG }}"
58+
59+
if [ -n "${PREVIOUS_TAG}" ]; then
60+
echo "Generating release notes from ${PREVIOUS_TAG} to ${STABLE_TAG}"
61+
NOTES=$(gh api repos/${{ github.repository }}/releases/generate-notes \
62+
-f tag_name="${STABLE_TAG}" \
63+
-f previous_tag_name="${PREVIOUS_TAG}" \
64+
--jq .body)
65+
else
66+
echo "No previous tag found, using automatic generation"
67+
NOTES=$(gh api repos/${{ github.repository }}/releases/generate-notes \
68+
-f tag_name="${STABLE_TAG}" \
69+
--jq .body)
70+
fi
71+
72+
# Save to output
73+
echo "notes<<EOF" >> $GITHUB_OUTPUT
74+
echo "$NOTES" >> $GITHUB_OUTPUT
75+
echo "EOF" >> $GITHUB_OUTPUT
76+
77+
- name: Create GitHub Release (if not dry run)
78+
if: ${{ !inputs.dry_run }}
79+
uses: softprops/action-gh-release@v2
80+
with:
81+
tag_name: ${{ steps.approve.outputs.STABLE_TAG }}
82+
name: ${{ steps.approve.outputs.STABLE_TAG }}
83+
draft: false
84+
prerelease: false
85+
body: ${{ steps.release_notes.outputs.notes }}
86+
token: ${{ secrets.LANCE_RELEASE_TOKEN }}
87+
88+
- name: Summary
89+
run: |
90+
echo "## Stable Release Summary" >> $GITHUB_STEP_SUMMARY
91+
echo "" >> $GITHUB_STEP_SUMMARY
92+
echo "- **RC Tag:** ${{ inputs.rc_tag }}" >> $GITHUB_STEP_SUMMARY
93+
echo "- **Stable Version:** ${{ steps.approve.outputs.STABLE_VERSION }}" >> $GITHUB_STEP_SUMMARY
94+
echo "- **Stable Tag:** ${{ steps.approve.outputs.STABLE_TAG }}" >> $GITHUB_STEP_SUMMARY
95+
echo "- **Release Branch:** ${{ steps.approve.outputs.RELEASE_BRANCH }}" >> $GITHUB_STEP_SUMMARY
96+
echo "- **Next Version:** ${{ steps.approve.outputs.NEXT_BETA_VERSION }}" >> $GITHUB_STEP_SUMMARY
97+
echo "- **Release Type:** $( [ "${{ steps.approve.outputs.IS_MAJOR_MINOR }}" == "true" ] && echo "Major/Minor" || echo "Patch" )" >> $GITHUB_STEP_SUMMARY
98+
if [ -n "${{ steps.approve.outputs.PREVIOUS_TAG }}" ]; then
99+
echo "- **Release Notes From:** ${{ steps.approve.outputs.PREVIOUS_TAG }}" >> $GITHUB_STEP_SUMMARY
100+
fi
101+
echo "- **Dry Run:** ${{ inputs.dry_run }}" >> $GITHUB_STEP_SUMMARY
102+
103+
if [ "${{ inputs.dry_run }}" == "true" ]; then
104+
echo "" >> $GITHUB_STEP_SUMMARY
105+
echo "⚠️ This was a dry run. No changes were pushed." >> $GITHUB_STEP_SUMMARY
106+
else
107+
echo "" >> $GITHUB_STEP_SUMMARY
108+
echo "✅ Stable release ${{ steps.approve.outputs.STABLE_TAG }} complete!" >> $GITHUB_STEP_SUMMARY
109+
echo "" >> $GITHUB_STEP_SUMMARY
110+
echo "**Publishing:** Stable artifacts will be published to PyPI, crates.io, Maven Central" >> $GITHUB_STEP_SUMMARY
111+
echo "" >> $GITHUB_STEP_SUMMARY
112+
echo "**Auto-bumped:** Release branch bumped to ${{ steps.approve.outputs.NEXT_BETA_VERSION }}" >> $GITHUB_STEP_SUMMARY
113+
fi

.github/workflows/bump-version/action.yml

Lines changed: 0 additions & 47 deletions
This file was deleted.

.github/workflows/cargo-publish.yml

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,32 @@ jobs:
3535
working-directory: .
3636
steps:
3737
- uses: actions/checkout@v4
38+
- name: Check if stable release
39+
id: check_version
40+
run: |
41+
# Get the tag from the event
42+
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
43+
TAG="${{ github.event.inputs.tag }}"
44+
else
45+
TAG="${{ github.ref_name }}"
46+
fi
47+
echo "Checking tag: $TAG"
48+
49+
# Skip if tag contains -beta or -rc (not a stable release)
50+
if [[ "$TAG" == *-beta.* ]] || [[ "$TAG" == *-rc.* ]]; then
51+
echo "Skipping cargo publish for non-stable version: $TAG"
52+
echo "Only stable versions (without -beta or -rc) are published to crates.io"
53+
echo "skip=true" >> $GITHUB_OUTPUT
54+
else
55+
echo "Stable version detected: $TAG"
56+
echo "skip=false" >> $GITHUB_OUTPUT
57+
fi
3858
- uses: Swatinem/rust-cache@v2
59+
if: steps.check_version.outputs.skip != 'true'
3960
with:
4061
workspaces: rust
4162
- name: Verify and checkout specified tag
42-
if: github.event_name == 'workflow_dispatch'
63+
if: github.event_name == 'workflow_dispatch' && steps.check_version.outputs.skip != 'true'
4364
run: |
4465
git fetch --all --tags
4566
if git rev-parse ${{ github.event.inputs.tag }} >/dev/null 2>&1; then
@@ -52,13 +73,15 @@ jobs:
5273
exit 1
5374
fi
5475
- name: Install dependencies
76+
if: steps.check_version.outputs.skip != 'true'
5577
run: |
5678
sudo apt update
5779
sudo apt install -y protobuf-compiler libssl-dev
5880
# Wait until https://github.com/rust-lang/crates-io-auth-action/issues/51 fixed
5981
# - uses: rust-lang/crates-io-auth-action@v1
6082
# id: auth
6183
- uses: albertlockett/[email protected]
84+
if: steps.check_version.outputs.skip != 'true'
6285
with:
6386
# registry-token: ${{ steps.auth.outputs.token }}
6487
registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}

0 commit comments

Comments
 (0)