This document describes in detail how to create a release of the Fluss clients (fluss-rust, fluss-python, fluss-cpp) from the fluss-rust repository. It is based on the Creating a Fluss Release guide of the Apache Fluss project and the release guide of Apache OpenDAL; releases are source archives plus CI-published crates.io and PyPI.
Publishing software has legal consequences. This guide complements the foundation-wide Product Release Policy and Release Distribution Policy.
The release process consists of:
Deciding to release and selecting a Release Manager is the first step. This is a consensus-based decision of the community.
Anybody can propose a release (e.g. on the dev mailing list), giving a short rationale and nominating a committer as Release Manager (including themselves). Any objections should be resolved by consensus before starting.
Checklist to proceed
Before your first release, perform one-time configuration. See Release Manager Preparation (GPG key, etc.). For fluss-rust you do not need Nexus/Maven; you only need GPG for signing the source archive and (optionally) git signing.
Checklist (one-time)
The release script (just release or ./scripts/release.sh) uses git archive and gpg; building or verifying the project locally requires Rust. Install the Rust toolchain (the version should match rust-toolchain.toml in the repo). The dependency list script (scripts/dependencies.py) requires Python 3.11+.
rustc --version cargo --version
To use just release, install just (e.g. cargo install just or your system package manager). If you prefer not to use just, run ./scripts/release.sh $RELEASE_VERSION instead.
If the project uses GitHub milestones for release tracking, create a new milestone for the next version (e.g. v0.2 if you are releasing 0.1.x). This helps contributors target issues to the correct release.
Check open issues that might block the release. Resolve, defer to the next milestone, or mark as blocker and do not proceed until they are fixed.
Use a clean clone to avoid local changes affecting the release.
git clone https://github.com/apache/fluss-rust.git cd fluss-rust
Set these once and use them in all following commands. (Bash syntax.)
RELEASE_VERSION="0.1.0" SHORT_RELEASE_VERSION="0.1" RELEASE_TAG="v${RELEASE_VERSION}" SVN_RELEASE_DIR="fluss-rust-${RELEASE_VERSION}" # Only set if there is a previous release (for compare link in DISCUSS / release notes) LAST_VERSION="0.0.9" NEXT_VERSION="0.2.0"
For the first release there is no previous version; leave LAST_VERSION unset or omit it when using the compare link in the DISCUSS thread and release notes.
ASF release policy requires that every release comply with ASF licensing policy and that an audit be performed before a full release. Generating and committing a dependency list (and using cargo-deny) documents third-party components and supports this requirement.
Do this on main before creating the release branch. Then both the release branch (when created from main) and main will have the same dependency list.
main:git checkout main git pull python3 scripts/dependencies.py generate git add **/DEPENDENCIES*.tsv # Bash: run shopt -s globstar first so ** matches subdirs git commit -m "chore: update dependency list for release ${RELEASE_VERSION}" git push origin main
To only check licenses (no file update): python3 scripts/dependencies.py check.
On Fluss Discussions or the dev list:
[DISCUSS] Release Apache Fluss clients (fluss-rust, fluss-python, fluss-cpp) $RELEASE_VERSIONhttps://github.com/apache/fluss-rust/compare/v${LAST_VERSION}...main. Ask for comments.From main, create a release branch. All release artifacts will be built from this branch. The tag (RC or release) is created later when building the release candidate.
git checkout main git pull git checkout -b release-${SHORT_RELEASE_VERSION} git push origin release-${SHORT_RELEASE_VERSION}
Do not create or push the release/RC tag yet; that happens in Build a release candidate after the source artifacts are staged.
So that main moves to the next version immediately after the release branch is cut, run the bump script and commit:
git checkout main git pull ./scripts/bump-version.sh $RELEASE_VERSION $NEXT_VERSION git add Cargo.toml git commit -m "Bump version to ${NEXT_VERSION}" git push origin main
The script updates the root Cargo.toml ([workspace.package] and [workspace.dependencies] fluss-rs). crates/fluss and bindings inherit version from the workspace.
You can open a pull request in the Apache Fluss repository for the release blog (announcement). If the project website has a download page, also create a PR to add the new version there. Do not merge these PRs until the release is finalized.
Checklist to proceed to the next step
NEXT_VERSION and pushedEach release candidate is built from the release branch, signed, and staged to the dev area of dist.apache.org. If an RC fails the vote, fix issues and repeat this section with an incremented RC_NUM (see Fix any issues).
Set these when building a release candidate. Start with RC_NUM=1; if the vote fails and you build a new candidate, increment to 2, then 3, etc.
export RC_NUM="1" export RC_TAG="v${RELEASE_VERSION}-rc${RC_NUM}" export SVN_RC_DIR="fluss-rust-${RELEASE_VERSION}-rc${RC_NUM}"
For a direct release (no RC), skip these and use RELEASE_TAG and SVN_RELEASE_DIR from the Prepare step instead.
Check out the release branch at the commit you want to release, create the signed tag, then push it. Use RC_TAG for a release candidate or RELEASE_TAG for a direct release. Pushing the tag triggers GitHub Actions (for an RC tag, fluss-python is published to TestPyPI).
git checkout release-${SHORT_RELEASE_VERSION} git pull git tag -s $RC_TAG -m "${RC_TAG}" git push origin $RC_TAG
Check CI: Actions (Release Rust, Release Python).
From the repository root (on the release branch, at the commit you tagged):
just release $RELEASE_VERSION # Or: ./scripts/release.sh $RELEASE_VERSION
This creates under dist/:
fluss-rust-${RELEASE_VERSION}-incubating.tgzfluss-rust-${RELEASE_VERSION}-incubating.tgz.sha512fluss-rust-${RELEASE_VERSION}-incubating.tgz.asc(Incubator policy requires the word “incubating” in release artifact names.)
Verify with: gpg --verify dist/fluss-rust-${RELEASE_VERSION}-incubating.tgz.asc dist/fluss-rust-${RELEASE_VERSION}-incubating.tgz
From the fluss-rust repo root, check out the Fluss dev area and add the release artifacts.
svn checkout https://dist.apache.org/repos/dist/dev/incubator/fluss fluss-dist-dev --depth=immediates cd fluss-dist-dev mkdir $SVN_RC_DIR cp ../dist/fluss-rust-${RELEASE_VERSION}-incubating.* $SVN_RC_DIR/ svn add $SVN_RC_DIR svn status svn commit -m "Add fluss-rust ${RELEASE_VERSION} RC${RC_NUM}"
Verify: https://dist.apache.org/repos/dist/dev/incubator/fluss/
Checklist to proceed to the next step
dist/$SVN_RC_DIRShare the release candidate for community review. If the project is in incubation, a two-phase vote (Fluss community then Incubator PMC) may be required; otherwise one community vote is enough.
Start the vote on the dev@ mailing list.
Subject: [VOTE] Release Apache Fluss clients (fluss-rust, fluss-python, fluss-cpp) ${RELEASE_VERSION} (RC${RC_NUM})
Body template:
Hi everyone,
Please review and vote on release candidate #${RC_NUM} for Apache Fluss clients (fluss-rust, fluss-python, fluss-cpp) ${RELEASE_VERSION}.
[ ] +1 Approve the release
[ ] +0 No opinion
[ ] -1 Do not approve (please provide specific comments)
The release candidate (source distribution) is available at:
* https://dist.apache.org/repos/dist/dev/incubator/fluss/$SVN_RC_DIR/
KEYS for signature verification:
* https://downloads.apache.org/incubator/fluss/KEYS
Git tag:
* https://github.com/apache/fluss-rust/releases/tag/$RC_TAG
PyPI (release) / TestPyPI (RC):
* https://pypi.org/project/pyfluss/
* https://test.pypi.org/project/pyfluss/
Please download, verify, and test. Verification steps are in [How to Verify a Release Candidate](verifying-a-release-candidate.md).
The vote will be open for at least 72 hours. It is adopted by majority approval with at least 3 PPMC affirmative votes (or as per project policy).
Thanks,
Release Manager
If issues are found, cancel the vote and go to Fix any issues. If the vote passes, close it and tally the result in a follow-up:
Subject: [RESULT][VOTE] Release Apache Fluss clients ${RELEASE_VERSION} (RC${RC_NUM})
Body: Summarize binding and non-binding votes and link to the vote thread.
If the project is in incubation, start a vote on general@incubator.apache.org after the Fluss community vote passes. Use the same structure: link to the community vote thread, release candidate URL, KEYS, tag, and ask IPMC to vote within 72 hours. Then send the result to the same list.
Checklist to proceed to finalization
If the vote revealed issues:
main (or the release branch) via normal PRs; cherry-pick fixes into the release branch as needed.svn checkout https://dist.apache.org/repos/dist/dev/incubator/fluss fluss-dist-dev --depth=immediates cd fluss-dist-dev svn remove $SVN_RC_DIR svn commit -m "Remove fluss-rust ${RELEASE_VERSION} RC${RC_NUM} (superseded)"
RC_NUM (e.g. set RC_NUM="2"), recreate RC_TAG and SVN_RC_DIR, then go back to Build a release candidate and repeat until a candidate is approved.Checklist
Once a release candidate has been approved, finalize the release.
If the community voted on an RC tag, create and push the formal release tag so CI publishes to crates.io and PyPI:
git checkout $RC_TAG git tag -s $RELEASE_TAG -m "Release fluss-rust, fluss-python, fluss-cpp ${RELEASE_VERSION}" git push origin $RELEASE_TAG
Move the staged artifacts from dev to release:
svn mv -m "Release fluss-rust ${RELEASE_VERSION}" \ https://dist.apache.org/repos/dist/dev/incubator/fluss/$SVN_RC_DIR \ https://dist.apache.org/repos/dist/release/incubator/fluss/$SVN_RELEASE_DIR
(Only PPMC members may have write access to the release repository; if you get permission errors, ask on the mailing list.)
Clean up the dev area so only the current RC or the moved release remains:
cd fluss-dist-dev svn remove $SVN_RC_DIR svn commit -m "Remove RC after release fluss-rust ${RELEASE_VERSION}"
$RELEASE_VERSION$RELEASE_VERSION$RELEASE_TAG.release-${RELEASE_VERSION} (i.e., the branch/commit used to create $RELEASE_TAG).https://downloads.apache.org/incubator/fluss/fluss-rust-${RELEASE_VERSION}/ (or the project download page). In the release description, include checksums and GPG verification steps.Add an entry for $RELEASE_VERSION with the list of changes (use Generate Release Note from the release tag). Commit and push to main.
Checklist to proceed to promotion
Merge the pull requests for the release blog and download page that were created in Prepare for the release.
Wait at least 24 hours after finalizing, per ASF release policy.
Use the @apache.org email address and plain text for the body; otherwise the list may reject the message.
Subject: [ANNOUNCE] Release Apache Fluss clients (fluss-rust, fluss-python, fluss-cpp) ${RELEASE_VERSION}
Body template:
The Apache Fluss community is pleased to announce the release of Apache Fluss clients (fluss-rust, fluss-python, fluss-cpp) ${RELEASE_VERSION}.
This release includes ...
(Notable changes; link to CHANGELOG or release notes.)
Download and verification:
* https://downloads.apache.org/incubator/fluss/$SVN_RELEASE_DIR/
* KEYS: https://downloads.apache.org/incubator/fluss/KEYS (or https://downloads.apache.org/fluss/KEYS after graduation)
Rust: cargo add fluss-rs
Python: pip install pyfluss
C++: build from source (see project documentation)
Release notes: https://github.com/apache/fluss-rust/releases/tag/$RELEASE_TAG
Thanks to all contributors!
Release Manager
Checklist to declare the process completed
After finishing the release, consider what could be improved (simplifications, clearer steps, automation). Propose changes on the dev list or via a pull request to this guide.