You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@fluss.apache.org by "luoyuxia (via GitHub)" <gi...@apache.org> on 2026/02/07 11:00:41 UTC
[PR] build: add how to release doc [fluss-rust]
luoyuxia opened a new pull request, #268:
URL: https://github.com/apache/fluss-rust/pull/268
<!--
*Thank you very much for contributing to Fluss - we are happy that you want to help us improve Fluss. To help the community review your contribution in the best possible way, please go through the checklist below, which will get the contribution into a shape in which it can be best reviewed.*
## Contribution Checklist
- Make sure that the pull request corresponds to a [GitHub issue](https://github.com/apache/fluss-rust/issues). Exceptions are made for typos in JavaDoc or documentation files, which need no issue.
- Name the pull request in the format "[component] Title of the pull request", where *[component]* should be replaced by the name of the component being changed. Typically, this corresponds to the component label assigned to the issue (e.g., [kv], [log], [client], [flink]). Skip *[component]* if you are unsure about which is the best component.
- Fill out the template below to describe the changes contributed by the pull request. That will give reviewers the context they need to do the review.
- Make sure that the change passes the automated tests, i.e., `mvn clean verify` passes.
- Each pull request should address only one issue, not mix up code from multiple issues.
**(The sections below can be removed for hotfixes or typos)**
-->
### Purpose
<!-- Linking this pull request to the issue -->
Linked issue: close #210
<!-- What is the purpose of the change -->
### Brief change log
<!-- Please describe the changes made in this pull request and explain how they address the issue -->
### Tests
<!-- List UT and IT cases to verify this change -->
### API and Format
<!-- Does this change affect API or storage format -->
### Documentation
<!-- Does this change introduce a new feature -->
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@fluss.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] build: add how to release doc [fluss-rust]
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #268:
URL: https://github.com/apache/fluss-rust/pull/268#discussion_r2777486096
##########
.github/workflows/release_python.yml:
##########
@@ -0,0 +1,152 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Publish the fluss Python binding to PyPI.
+# Trigger: push tag only (e.g. v0.1.0).
+# Pre-release tags (containing '-') publish to TestPyPI; release tags publish to PyPI.
+#
+# Token auth: set repo variable PYPI_USE_TOKEN_AUTH = 'true' and add secrets PYPI_API_TOKEN / TEST_PYPI_API_TOKEN.
+# Trusted Publishing (OIDC): leave PYPI_USE_TOKEN_AUTH unset; do not pass password so the action uses OIDC.
+
+name: Release Python
+
+on:
+ push:
+ tags:
+ - "*"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+
+jobs:
+ sdist:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install protoc
+ run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
+
+ - uses: PyO3/maturin-action@v1
+ with:
+ working-directory: bindings/python
+ command: sdist
+ args: -o dist
+
+ - name: Upload sdist
+ uses: actions/upload-artifact@v4
+ with:
+ name: wheels-sdist
+ path: bindings/python/dist
+
+ wheels:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ include:
+ - { os: windows-latest }
+ - { os: macos-15-intel, target: "x86_64-apple-darwin" }
+ - { os: macos-15, target: "aarch64-apple-darwin" }
+ - { os: ubuntu-latest, target: "x86_64" }
+ - { os: ubuntu-latest, target: "aarch64", manylinux: "manylinux_2_28" }
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install protoc (Linux)
+ if: runner.os == 'Linux'
+ run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
+
+ - name: Install protoc (macOS)
+ if: runner.os == 'macOS'
+ run: brew install protobuf
+
+ - name: Install protoc (Windows)
+ if: runner.os == 'Windows'
+ run: choco install protobuf -y
+ shell: pwsh
+
+ - uses: PyO3/maturin-action@v1
+ with:
+ working-directory: bindings/python
+ target: ${{ matrix.target }}
+ command: build
+ args: --release -o dist -i python3.11
+ manylinux: ${{ matrix.manylinux || 'auto' }}
+ - uses: PyO3/maturin-action@v1
+ with:
+ working-directory: bindings/python
+ target: ${{ matrix.target }}
+ command: build
+ args: --release -o dist -i python3.12
+ manylinux: ${{ matrix.manylinux || 'auto' }}
Review Comment:
Wheel builds are only generated for Python 3.11 and 3.12, but `pyproject.toml` declares support for Python 3.9+ (and includes 3.9/3.10 classifiers). With the current setup, 3.9/3.10 users will fall back to building from sdist (requiring a Rust toolchain, protoc, etc.). Consider adding wheel builds for 3.9 and 3.10 or narrowing `requires-python`/classifiers to match what you ship.
##########
scripts/constants.py:
##########
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import tomllib
+from pathlib import Path
+
+ROOT_DIR = Path(__file__).resolve().parent.parent
+
+
+def list_packages():
+ """Package directories from [workspace].members in root Cargo.toml, plus workspace root.
+ Each gets a DEPENDENCIES.rust.tsv. Avoids scanning target/, .git/, etc.
+ """
+ root_cargo = ROOT_DIR / "Cargo.toml"
+ if not root_cargo.exists():
+ return ["."]
+ with open(root_cargo, "rb") as f:
+ data = tomllib.load(f)
Review Comment:
`tomllib` is only available in Python 3.11+. Since the release docs invoke this script via `python3`, this will fail on Python 3.9/3.10 (which are still commonly installed and also listed as supported for the Python bindings). Consider using a `tomli` fallback for <3.11 or explicitly documenting/enforcing a Python >=3.11 requirement for the release tooling.
##########
docs/creating-a-release.md:
##########
@@ -0,0 +1,452 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+# Creating a Fluss Rust Client Release
+
+This document describes in detail how to create a release of the **Fluss clients** (fluss-rust, fluss-python, fluss-cpp) from the [fluss-rust](https://github.com/apache/fluss-rust) repository. It is based on the [Creating a Fluss Release](https://fluss.apache.org/community/how-to-release/creating-a-fluss-release/) guide of the Apache Fluss project and the [release guide of Apache OpenDAL](https://nightlies.apache.org/opendal/opendal-docs-stable/community/release/); 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](https://www.apache.org/legal/release-policy.html) and [Release Distribution Policy](https://infra.apache.org/release-distribution.html).
+
+## Overview
+
+
+
+The release process consists of:
+
+1. [Decide to release](#decide-to-release)
+2. [Prepare for the release](#prepare-for-the-release)
+3. [Build a release candidate](#build-a-release-candidate)
+4. [Vote on the release candidate](#vote-on-the-release-candidate)
+5. [If necessary, fix any issues and go back to step 3](#fix-any-issues)
+6. [Finalize the release](#finalize-the-release)
+7. [Promote the release](#promote-the-release)
+
+## Decide to release
+
+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**
+
+- [ ] Community agrees to release
+- [ ] A Release Manager is selected
+
+## Prepare for the release
+
+### 0. One-time Release Manager setup
+
+Before your first release, perform one-time configuration. See **[Release Manager Preparation](https://fluss.apache.org/community/how-to-release/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)**
+
+- [ ] GPG key set up and published to [KEYS](https://downloads.apache.org/incubator/fluss/KEYS) or Apache account
+- [ ] Git configured to use your GPG key for signing tags
+
+### 1. Install Rust (and optional: just)
+
+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](https://rustup.rs/) (the version should match [rust-toolchain.toml](https://github.com/apache/fluss-rust/blob/main/rust-toolchain.toml) in the repo).
+
+```bash
+rustc --version
+cargo --version
+```
+
+To use `just release`, install [just](https://github.com/casey/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.
+
+### 2. Optional: Create a new Milestone in GitHub
+
+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.
+
+### 3. Optional: Triage release-blocking issues
+
+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.
+
+### 4. Clone fluss-rust into a fresh workspace
+
+Use a clean clone to avoid local changes affecting the release.
+
+```bash
+git clone https://github.com/apache/fluss-rust.git
+cd fluss-rust
+```
+
+### 5. Set up environment variables
+
+Set these once and use them in all following commands. (Bash syntax.)
+
+```bash
+export RELEASE_VERSION="0.1.0"
+export RELEASE_TAG="v${RELEASE_VERSION}"
+export SVN_RELEASE_DIR="fluss-rust-${RELEASE_VERSION}"
+# Only set if there is a previous release (for compare link in DISCUSS / release notes)
+export LAST_VERSION="0.0.9"
+export 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.
+
+### 6. Generate dependencies list
+
+[ASF release policy](https://www.apache.org/legal/release-policy.html) requires that every release comply with [ASF licensing policy](https://www.apache.org/legal/resolved.html) 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.
+
+1. Download and set up [cargo-deny](https://embarkstudios.github.io/cargo-deny/cli/index.html) (see cargo-deny docs).
+2. Run the script to update the dependency list, then commit on `main`:
+
+```bash
+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`.
+
+### 7. Optional: Start a [DISCUSS] thread
+
+On [Fluss Discussions](https://github.com/apache/fluss-rust/discussions) or the dev list:
+
+- **Subject:** `[DISCUSS] Release Apache Fluss clients (fluss-rust, fluss-python, fluss-cpp) $RELEASE_VERSION`
+- **Body:** Short rationale; if there is a previous release, add compare link: `https://github.com/apache/fluss-rust/compare/v${LAST_VERSION}...main`. Ask for comments.
+
+### 8. Create a release branch
+
+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.
+
+```bash
+git checkout main
+git pull
+git checkout -b release-${RELEASE_VERSION}
+git push origin release-${RELEASE_VERSION}
+```
+
+Do **not** create or push the release/RC tag yet; that happens in [Build a release candidate](#build-a-release-candidate) after the source artifacts are staged.
+
+### 9. Bump version on main for the next development cycle
+
+So that `main` moves to the next version immediately after the release branch is cut, run the bump script and commit:
+
+```bash
+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.
+
+### 10. Optional: Create PRs for release blog and download page
+
+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**
+
+- [ ] Rust (and optionally just) installed and on PATH
+- [ ] No release-blocking issues (or triaged)
+- [ ] Environment variables set
+- [ ] Release branch created and pushed
+- [ ] Main branch bumped to `NEXT_VERSION` and pushed
+- [ ] Dependencies list generated and committed on main
+- [ ] (Optional) DISCUSS thread and/or tracking issue created
+- [ ] (Optional) PRs for blog and download page created but not merged
+
+## Build a release candidate
+
+Each 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](#fix-any-issues)).
+
+### 1. Set RC environment variables
+
+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.
+
+```bash
+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.
+
+### 2. Check out the release branch and create the tag
+
+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).
+
+```bash
+git checkout release-${RELEASE_VERSION}
+git pull
+git tag -s $RC_TAG -m "${RC_TAG}"
+git push origin $RC_TAG
+```
+
+Check CI: [Actions](https://github.com/apache/fluss-rust/actions) (Release Rust, Release Python).
+
+### 3. Create source release artifacts
+
+From the repository root (on the release branch, at the commit you tagged):
+
+```bash
+just release $RELEASE_VERSION
+# Or: ./scripts/release.sh $RELEASE_VERSION
+```
+
+This creates under `dist/`:
+
+- `fluss-rust-${RELEASE_VERSION}.tar.gz`
+- `fluss-rust-${RELEASE_VERSION}.tar.gz.sha512`
+- `fluss-rust-${RELEASE_VERSION}.tar.gz.asc`
+
+Verify with: `gpg --verify dist/fluss-rust-${RELEASE_VERSION}.tar.gz.asc dist/fluss-rust-${RELEASE_VERSION}.tar.gz`
+
+### 4. Stage artifacts to SVN (dist.apache.org dev)
+
+From the **fluss-rust** repo root, check out the Fluss dev area and add the release artifacts.
+
+```bash
+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}.* $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/](https://dist.apache.org/repos/dist/dev/incubator/fluss/)
+
+---
+
+**Checklist to proceed to the next step**
+
+- [ ] Source distribution built and signed under `dist/`
+- [ ] Artifacts staged to [dist.apache.org dev](https://dist.apache.org/repos/dist/dev/incubator/fluss/) under `$SVN_RC_DIR`
+- [ ] RC (or release) tag pushed to GitHub
+- [ ] CI for Release Rust / Release Python succeeded
+
+## Vote on the release candidate
+
+Share the release candidate for community review. If the project is in incubation, a [two-phase vote](https://incubator.apache.org/cookbook/#two_phase_vote_on_podling_releases) (Fluss community then Incubator PMC) may be required; otherwise one community vote is enough.
+
+### Fluss community vote
+
+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 the project docs (todo: add how to verify release).
+
+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](#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.
+
+### Incubator PMC vote (if applicable)
+
+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**
+
+- [ ] Community vote passed (at least 3 binding +1, more +1 than -1)
+- [ ] (If incubating) Incubator PMC vote passed
+
+## Fix any issues
+
+If the vote revealed issues:
+
+1. Fix them on `main` (or the release branch) via normal PRs; cherry-pick fixes into the release branch as needed.
+2. Remove the old RC from dist.apache.org dev (optional but recommended):
+
+```bash
+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)"
+```
+
+3. Increment `RC_NUM` (e.g. set `RC_NUM="2"`), recreate `RC_TAG` and `SVN_RC_DIR`, then go back to [Build a release candidate](#build-a-release-candidate) and repeat until a candidate is approved.
+
+**Checklist**
+
+- [ ] Issues resolved and changes merged/cherry-picked to the release branch
+- [ ] New RC built and voted on (or same RC re-voted if only minor fixes)
+
+## Finalize the release
+
+Once a release candidate has been approved, finalize the release.
+
+### 1. Push the release git tag (if the vote was on an RC)
+
+If the community voted on an RC tag, create and push the formal release tag so CI publishes to crates.io and PyPI:
+
+```bash
+git checkout $RC_TAG
+git tag -s $RELEASE_TAG -m "Release fluss-rust, fluss-python, fluss-cpp ${RELEASE_VERSION}"
+git push origin $RELEASE_TAG
+```
+
+### 2. Deploy source artifacts to the release repository
+
+Move the staged artifacts from dev to release:
+
+```bash
+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.)
+
+### 3. Remove old RC(s) from dev (optional)
+
+Clean up the dev area so only the current RC or the moved release remains:
+
+```bash
+cd fluss-dist-dev
+svn remove $SVN_RC_DIR
+svn commit -m "Remove RC after release fluss-rust ${RELEASE_VERSION}"
+```
+
+### 4. Verify language artifacts
+
+- **fluss-rust:** [crates.io/crates/fluss-rs](https://crates.io/crates/fluss-rs) shows version `$RELEASE_VERSION`
+- **fluss-python:** [PyPI – pyfluss](https://pypi.org/project/pyfluss/) shows version `$RELEASE_VERSION`
+- **fluss-cpp:** Distributed via the source archive; no separate registry
+
+### 5. Create GitHub Release
+
+1. Go to [Releases → New release](https://github.com/apache/fluss-rust/releases/new).
+2. Choose tag `$RELEASE_TAG`.
+3. Set the target to the release branch `release-${RELEASE_VERSION}` (i.e., the branch/commit used to create `$RELEASE_TAG`).
+4. Click **Generate release notes**, then add: notable changes, breaking changes (if any) from component upgrade docs, **official download link** (source archive and verification), and install instructions for fluss-rust, fluss-python, fluss-cpp.
+ - **Download link:** `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.
+5. Click **Publish release**.
+
+### 6. Update CHANGELOG.md on main
+
+Add an entry for `$RELEASE_VERSION` with the list of changes (use [Generate Release Note](generate-release-note.md) from the release tag). Commit and push to `main`.
+
+---
+
+**Checklist to proceed to promotion**
+
+- [ ] Release tag pushed; CI published to crates.io and PyPI
+- [ ] Source artifacts in [dist release](https://dist.apache.org/repos/dist/release/incubator/fluss/)
+- [ ] GitHub Release created
+- [ ] CHANGELOG.md updated on main
+
+## Promote the release
+
+### Merge website PRs
+
+Merge the pull requests for the release blog and download page that were created in [Prepare for the release](#9-optional-create-prs-for-release-blog-and-download-page).
Review Comment:
This internal link target appears to be off by one: the referenced heading is "### 10. Optional: Create PRs for release blog and download page", so `#9-optional-...` likely won’t resolve on GitHub. Update the anchor to match the actual heading so the link works.
```suggestion
Merge the pull requests for the release blog and download page that were created in [Prepare for the release](#10-optional-create-prs-for-release-blog-and-download-page).
```
##########
scripts/bump-version.sh:
##########
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Bump version in root Cargo.toml ([workspace.package] and [workspace.dependencies] fluss-rs).
+# Run from repo root. Use after cutting a release branch so main is set to the next version.
+#
+# Usage: ./scripts/bump-version.sh <current_version> <next_version>
+# e.g. ./scripts/bump-version.sh 0.1.0 0.1.1
+# Or with env vars: ./scripts/bump-version.sh $RELEASE_VERSION $NEXT_VERSION
+
+set -e
+
+if [ -z "$1" ] || [ -z "$2" ]; then
+ echo "Usage: $0 <current_version> <next_version>"
+ echo " e.g. $0 0.1.0 0.1.1"
+ exit 1
+fi
+
+FROM_VERSION="$1"
+TO_VERSION="$2"
+
+REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+cd "$REPO_ROOT"
+
+if [ ! -f Cargo.toml ]; then
+ echo "Cargo.toml not found. Run from repo root."
+ exit 1
+fi
+
+# Replace version = "X.Y.Z" with version = "TO_VERSION" (all occurrences in root Cargo.toml)
+case "$(uname -s)" in
+ Darwin)
+ sed -i '' "s/version = \"${FROM_VERSION}\"/version = \"${TO_VERSION}\"/g" Cargo.toml
+ ;;
+ *)
+ sed -i "s/version = \"${FROM_VERSION}\"/version = \"${TO_VERSION}\"/g" Cargo.toml
+ ;;
+esac
+
Review Comment:
The header comment says this bumps only `[workspace.package]` and the `fluss-rs` entry under `[workspace.dependencies]`, but the `sed` replacement updates *every* `version = "${FROM_VERSION}"` occurrence in the root `Cargo.toml`. This can unintentionally bump unrelated dependency versions if they happen to match `FROM_VERSION`. Consider scoping the replacement to the intended sections/keys (or using a TOML-aware edit) and failing if the expected fields weren’t updated.
```suggestion
# Replace version in [workspace.package] and fluss-rs under [workspace.dependencies] only.
TMP_CARGO_TOML="$(mktemp "${TMPDIR:-/tmp}/cargo.toml.XXXXXX")"
if ! awk -v from="$FROM_VERSION" -v to="$TO_VERSION" '
BEGIN {
in_ws_package = 0;
in_ws_deps = 0;
pkg_version_updated = 0;
fluss_version_updated = 0;
}
/^\[workspace\.package\]/ {
in_ws_package = 1;
in_ws_deps = 0;
}
/^\[workspace\.dependencies\]/ {
in_ws_package = 0;
in_ws_deps = 1;
}
/^\[/ && !/^\[workspace\.(package|dependencies)\]/ {
in_ws_package = 0;
in_ws_deps = 0;
}
{
if (in_ws_package && $1 == "version" && $2 == "=") {
gsub("\"" from "\"", "\"" to "\"");
if ($0 ~ "version[[:space:]]*=[[:space:]]*\"" to "\"") {
pkg_version_updated = 1;
}
} else if (in_ws_deps && $1 == "fluss-rs" && $2 == "=") {
gsub("version[[:space:]]*=[[:space:]]*\"" from "\"", "version = \"" to "\"");
if ($0 ~ "version[[:space:]]*=[[:space:]]*\"" to "\"") {
fluss_version_updated = 1;
}
}
print;
}
END {
if (!pkg_version_updated) {
printf "Error: did not update [workspace.package] version from %s to %s\n", from, to > "/dev/stderr";
exit 1;
}
if (!fluss_version_updated) {
printf "Error: did not update fluss-rs version under [workspace.dependencies] from %s to %s\n", from, to > "/dev/stderr";
exit 1;
}
}
' Cargo.toml > "$TMP_CARGO_TOML"; then
rm -f "$TMP_CARGO_TOML"
exit 1
fi
mv "$TMP_CARGO_TOML" Cargo.toml
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@fluss.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] build: add how to release doc [fluss-rust]
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #268:
URL: https://github.com/apache/fluss-rust/pull/268#discussion_r2777473601
##########
scripts/constants.py:
##########
@@ -0,0 +1,34 @@
+#!/usr/bin/env python3
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from pathlib import Path
+
+ROOT_DIR = Path(__file__).resolve().parent.parent
+
+
+def list_packages():
+ """All directories that have Cargo.toml (recursive); each gets a DEPENDENCIES.rust.tsv."""
+ packages = []
+ for cargo in sorted(ROOT_DIR.rglob("Cargo.toml")):
+ parent = cargo.parent
+ rel = "." if parent == ROOT_DIR else parent.relative_to(ROOT_DIR).as_posix()
+ packages.append(rel)
+ return packages
Review Comment:
`list_packages()` uses `ROOT_DIR.rglob("Cargo.toml")`, which will also walk local build/output directories (e.g. `target/`) and can become very slow or pick up unintended manifests. Consider excluding common dirs (`target`, `.git`, etc.) or deriving the package list from `[workspace].members` in the root `Cargo.toml`.
##########
docs/creating-a-release.md:
##########
@@ -0,0 +1,452 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+# Creating a Fluss Rust Client Release
+
+This document describes in detail how to create a release of the **Fluss clients** (fluss-rust, fluss-python, fluss-cpp) from the [fluss-rust](https://github.com/apache/fluss-rust) repository. It is based on the [Creating a Fluss Release](https://fluss.apache.org/community/how-to-release/creating-a-fluss-release/) guide of the Apache Fluss project and the [release guide of Apache OpenDAL](https://nightlies.apache.org/opendal/opendal-docs-stable/community/release/); releases are source archives plus CI-published crates.io and PyPI).
Review Comment:
There appears to be an extra closing parenthesis at the end of this sentence (after “PyPI)”). Please fix the punctuation so the paragraph reads cleanly.
```suggestion
This document describes in detail how to create a release of the **Fluss clients** (fluss-rust, fluss-python, fluss-cpp) from the [fluss-rust](https://github.com/apache/fluss-rust) repository. It is based on the [Creating a Fluss Release](https://fluss.apache.org/community/how-to-release/creating-a-fluss-release/) guide of the Apache Fluss project and the [release guide of Apache OpenDAL](https://nightlies.apache.org/opendal/opendal-docs-stable/community/release/); releases are source archives plus CI-published crates.io and PyPI.
```
##########
docs/creating-a-release.md:
##########
@@ -0,0 +1,452 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+# Creating a Fluss Rust Client Release
+
+This document describes in detail how to create a release of the **Fluss clients** (fluss-rust, fluss-python, fluss-cpp) from the [fluss-rust](https://github.com/apache/fluss-rust) repository. It is based on the [Creating a Fluss Release](https://fluss.apache.org/community/how-to-release/creating-a-fluss-release/) guide of the Apache Fluss project and the [release guide of Apache OpenDAL](https://nightlies.apache.org/opendal/opendal-docs-stable/community/release/); 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](https://www.apache.org/legal/release-policy.html) and [Release Distribution Policy](https://infra.apache.org/release-distribution.html).
+
+## Overview
+
+
+
+The release process consists of:
+
+1. [Decide to release](#decide-to-release)
+2. [Prepare for the release](#prepare-for-the-release)
+3. [Build a release candidate](#build-a-release-candidate)
+4. [Vote on the release candidate](#vote-on-the-release-candidate)
+5. [If necessary, fix any issues and go back to step 3](#fix-any-issues)
+6. [Finalize the release](#finalize-the-release)
+7. [Promote the release](#promote-the-release)
+
+## Decide to release
+
+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**
+
+- [ ] Community agrees to release
+- [ ] A Release Manager is selected
+
+## Prepare for the release
+
+### 0. One-time Release Manager setup
+
+Before your first release, perform one-time configuration. See **[Release Manager Preparation](https://fluss.apache.org/community/how-to-release/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)**
+
+- [ ] GPG key set up and published to [KEYS](https://downloads.apache.org/incubator/fluss/KEYS) or Apache account
+- [ ] Git configured to use your GPG key for signing tags
+
+### 1. Install Rust (and optional: just)
+
+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](https://rustup.rs/) (the version should match [rust-toolchain.toml](https://github.com/apache/fluss-rust/blob/main/rust-toolchain.toml) in the repo).
+
+```bash
+rustc --version
+cargo --version
+```
+
+To use `just release`, install [just](https://github.com/casey/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.
+
+### 2. Optional: Create a new Milestone in GitHub
+
+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.
+
+### 3. Optional: Triage release-blocking issues
+
+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.
+
+### 4. Clone fluss-rust into a fresh workspace
+
+Use a clean clone to avoid local changes affecting the release.
+
+```bash
+git clone https://github.com/apache/fluss-rust.git
+cd fluss-rust
+```
+
+### 5. Set up environment variables
+
+Set these once and use them in all following commands. (Bash syntax.)
+
+```bash
+export RELEASE_VERSION="0.1.0"
+export RELEASE_TAG="v${RELEASE_VERSION}"
+export SVN_RELEASE_DIR="fluss-rust-${RELEASE_VERSION}"
+# Only set if there is a previous release (for compare link in DISCUSS / release notes)
+export LAST_VERSION="0.0.9"
+export 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.
+
+### 6. Generate dependencies list
+
+[ASF release policy](https://www.apache.org/legal/release-policy.html) requires that every release comply with [ASF licensing policy](https://www.apache.org/legal/resolved.html) 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.
+
+1. Download and set up [cargo-deny](https://embarkstudios.github.io/cargo-deny/cli/index.html) (see cargo-deny docs).
+2. Run the script to update the dependency list, then commit on `main`:
+
+```bash
+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`.
+
+### 7. Optional: Start a [DISCUSS] thread
+
+On [Fluss Discussions](https://github.com/apache/fluss-rust/discussions) or the dev list:
+
+- **Subject:** `[DISCUSS] Release Apache Fluss clients (fluss-rust, fluss-python, fluss-cpp) $RELEASE_VERSION`
+- **Body:** Short rationale; if there is a previous release, add compare link: `https://github.com/apache/fluss-rust/compare/v${LAST_VERSION}...main`. Ask for comments.
+
+### 8. Create a release branch
+
+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.
+
+```bash
+git checkout main
+git pull
+git checkout -b release-${RELEASE_VERSION}
+git push origin release-${RELEASE_VERSION}
+```
+
+Do **not** create or push the release/RC tag yet; that happens in [Build a release candidate](#build-a-release-candidate) after the source artifacts are staged.
+
+### 9. Bump version on main for the next development cycle
+
+So that `main` moves to the next version immediately after the release branch is cut, run the bump script and commit:
+
+```bash
+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.
+
+### 10. Optional: Create PRs for release blog and download page
+
+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**
+
+- [ ] Rust (and optionally just) installed and on PATH
+- [ ] No release-blocking issues (or triaged)
+- [ ] Environment variables set
+- [ ] Release branch created and pushed
+- [ ] Main branch bumped to `NEXT_VERSION` and pushed
+- [ ] Dependencies list generated and committed on main
+- [ ] (Optional) DISCUSS thread and/or tracking issue created
+- [ ] (Optional) PRs for blog and download page created but not merged
+
+## Build a release candidate
+
+Each 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](#fix-any-issues)).
+
+### 1. Set RC environment variables
+
+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.
+
+```bash
+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.
+
+### 2. Check out the release branch and create the tag
+
+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).
+
+```bash
+git checkout release-${RELEASE_VERSION}
+git pull
+git tag -s $RC_TAG -m "${RC_TAG}"
+git push origin $RC_TAG
+```
+
+Check CI: [Actions](https://github.com/apache/fluss-rust/actions) (Release Rust, Release Python).
+
+### 3. Create source release artifacts
+
+From the repository root (on the release branch, at the commit you tagged):
+
+```bash
+just release $RELEASE_VERSION
+# Or: ./scripts/release.sh $RELEASE_VERSION
+```
+
+This creates under `dist/`:
+
+- `fluss-rust-${RELEASE_VERSION}.tar.gz`
+- `fluss-rust-${RELEASE_VERSION}.tar.gz.sha512`
+- `fluss-rust-${RELEASE_VERSION}.tar.gz.asc`
+
+Verify with: `gpg --verify dist/fluss-rust-${RELEASE_VERSION}.tar.gz.asc dist/fluss-rust-${RELEASE_VERSION}.tar.gz`
+
+### 4. Stage artifacts to SVN (dist.apache.org dev)
+
+From the **fluss-rust** repo root, check out the Fluss dev area and add the release artifacts.
+
+```bash
+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}.* $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/](https://dist.apache.org/repos/dist/dev/incubator/fluss/)
+
+---
+
+**Checklist to proceed to the next step**
+
+- [ ] Source distribution built and signed under `dist/`
+- [ ] Artifacts staged to [dist.apache.org dev](https://dist.apache.org/repos/dist/dev/incubator/fluss/) under `$SVN_RC_DIR`
+- [ ] RC (or release) tag pushed to GitHub
+- [ ] CI for Release Rust / Release Python succeeded
+
+## Vote on the release candidate
+
+Share the release candidate for community review. If the project is in incubation, a [two-phase vote](https://incubator.apache.org/cookbook/#two_phase_vote_on_podling_releases) (Fluss community then Incubator PMC) may be required; otherwise one community vote is enough.
+
+### Fluss community vote
+
+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 the project docs (todo: add how to verify release).
+
+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](#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.
+
+### Incubator PMC vote (if applicable)
+
+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**
+
+- [ ] Community vote passed (at least 3 binding +1, more +1 than -1)
+- [ ] (If incubating) Incubator PMC vote passed
+
+## Fix any issues
+
+If the vote revealed issues:
+
+1. Fix them on `main` (or the release branch) via normal PRs; cherry-pick fixes into the release branch as needed.
+2. Remove the old RC from dist.apache.org dev (optional but recommended):
+
+```bash
+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)"
+```
+
+3. Increment `RC_NUM` (e.g. set `RC_NUM="2"`), recreate `RC_TAG` and `SVN_RC_DIR`, then go back to [Build a release candidate](#build-a-release-candidate) and repeat until a candidate is approved.
+
+**Checklist**
+
+- [ ] Issues resolved and changes merged/cherry-picked to the release branch
+- [ ] New RC built and voted on (or same RC re-voted if only minor fixes)
+
+## Finalize the release
+
+Once a release candidate has been approved, finalize the release.
+
+### 1. Push the release git tag (if the vote was on an RC)
+
+If the community voted on an RC tag, create and push the formal release tag so CI publishes to crates.io and PyPI:
+
+```bash
+git checkout $RC_TAG
+git tag -s $RELEASE_TAG -m "Release fluss-rust, fluss-python, fluss-cpp ${RELEASE_VERSION}"
+git push origin $RELEASE_TAG
+```
+
+### 2. Deploy source artifacts to the release repository
+
+Move the staged artifacts from dev to release:
+
+```bash
+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.)
+
+### 3. Remove old RC(s) from dev (optional)
+
+Clean up the dev area so only the current RC or the moved release remains:
+
+```bash
+cd fluss-dist-dev
+svn remove $SVN_RC_DIR
+svn commit -m "Remove RC after release fluss-rust ${RELEASE_VERSION}"
+```
+
+### 4. Verify language artifacts
+
+- **fluss-rust:** [crates.io/crates/fluss-rs](https://crates.io/crates/fluss-rs) shows version `$RELEASE_VERSION`
+- **fluss-python:** [PyPI – pyfluss](https://pypi.org/project/pyfluss/) shows version `$RELEASE_VERSION`
+- **fluss-cpp:** Distributed via the source archive; no separate registry
+
+### 5. Create GitHub Release
+
+1. Go to [Releases → New release](https://github.com/apache/fluss-rust/releases/new).
+2. Choose tag `$RELEASE_TAG`.
+3. Set target branch to `main`.
Review Comment:
The instructions say to set the GitHub Release target branch to `main`, but the tag is created from `release-${RELEASE_VERSION}` / the RC tag commit. Targeting `main` can point the release UI at the wrong commit if `main` has moved on; it’s clearer to target the release branch or the tagged commit itself.
```suggestion
3. Set the target to the release branch `release-${RELEASE_VERSION}` (i.e., the branch/commit used to create `$RELEASE_TAG`), not `main`.
```
##########
.github/workflows/release_rust.yml:
##########
@@ -0,0 +1,58 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Publish the fluss Rust crate to crates.io.
+# Trigger: push tag only (e.g. v0.1.0).
+# Pre-release tags (containing '-') do not publish; release tags publish to crates.io.
+#
+# Token auth: set repo variable CARGO_USE_TOKEN_AUTH = 'true' and add secret CARGO_REGISTRY_TOKEN.
+# Trusted Publishing (OIDC): leave CARGO_USE_TOKEN_AUTH unset; token is obtained via OIDC (no secret).
+
+name: Release Rust
+
+on:
+ push:
+ tags:
+ - "*"
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install protoc
+ run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
+
+ - name: Dry run (crates/fluss)
+ run: cargo publish -p fluss-rs --dry-run
+
+ - name: Get crates.io token (OIDC)
+ if: startsWith(github.ref, 'refs/tags/') && !contains(github.ref, '-') && vars.CARGO_USE_TOKEN_AUTH != 'true'
+ uses: rust-lang/crates-io-auth-action@v1
+ id: auth
+ with:
+ token-type: publish
+
+ - name: Publish fluss-rs to crates.io
+ if: startsWith(github.ref, 'refs/tags/') && !contains(github.ref, '-')
+ run: cargo publish -p fluss-rs
+ env:
+ CARGO_REGISTRY_TOKEN: ${{ vars.CARGO_USE_TOKEN_AUTH == 'true' && secrets.CARGO_REGISTRY_TOKEN || steps.auth.outputs.token }}
Review Comment:
The `CARGO_REGISTRY_TOKEN` expression relies on `&& ... || ...` short-circuiting, which can behave unexpectedly if the secret is empty (it will fall through to `steps.auth.outputs.token`, which won't exist when the auth step is skipped). Prefer using a ternary expression so the token source is unambiguous.
```suggestion
CARGO_REGISTRY_TOKEN: ${{ vars.CARGO_USE_TOKEN_AUTH == 'true' ? secrets.CARGO_REGISTRY_TOKEN : steps.auth.outputs.token }}
```
##########
justfile:
##########
@@ -0,0 +1,28 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Create ASF source release artifacts under dist/.
+# Check out the release tag first (e.g. git checkout v0.1.0-rc.1).
Review Comment:
The RC tag example uses `v0.1.0-rc.1` here, but other docs/scripts use `v0.1.0-rc1` (no dot). Standardizing the tag format in examples will avoid confusion.
```suggestion
# Check out the release tag first (e.g. git checkout v0.1.0-rc1).
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@fluss.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] build: add how to release doc [fluss-rust]
Posted by "fresh-borzoni (via GitHub)" <gi...@apache.org>.
fresh-borzoni commented on code in PR #268:
URL: https://github.com/apache/fluss-rust/pull/268#discussion_r2777522207
##########
docs/creating-a-release.md:
##########
@@ -0,0 +1,452 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+# Creating a Fluss Rust Client Release
+
+This document describes in detail how to create a release of the **Fluss clients** (fluss-rust, fluss-python, fluss-cpp) from the [fluss-rust](https://github.com/apache/fluss-rust) repository. It is based on the [Creating a Fluss Release](https://fluss.apache.org/community/how-to-release/creating-a-fluss-release/) guide of the Apache Fluss project and the [release guide of Apache OpenDAL](https://nightlies.apache.org/opendal/opendal-docs-stable/community/release/); 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](https://www.apache.org/legal/release-policy.html) and [Release Distribution Policy](https://infra.apache.org/release-distribution.html).
+
+## Overview
+
+
+
+The release process consists of:
+
+1. [Decide to release](#decide-to-release)
+2. [Prepare for the release](#prepare-for-the-release)
+3. [Build a release candidate](#build-a-release-candidate)
+4. [Vote on the release candidate](#vote-on-the-release-candidate)
+5. [If necessary, fix any issues and go back to step 3](#fix-any-issues)
+6. [Finalize the release](#finalize-the-release)
+7. [Promote the release](#promote-the-release)
+
+## Decide to release
+
+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**
+
+- [ ] Community agrees to release
+- [ ] A Release Manager is selected
+
+## Prepare for the release
+
+### 0. One-time Release Manager setup
+
+Before your first release, perform one-time configuration. See **[Release Manager Preparation](https://fluss.apache.org/community/how-to-release/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)**
+
+- [ ] GPG key set up and published to [KEYS](https://downloads.apache.org/incubator/fluss/KEYS) or Apache account
+- [ ] Git configured to use your GPG key for signing tags
+
+### 1. Install Rust (and optional: just)
+
+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](https://rustup.rs/) (the version should match [rust-toolchain.toml](https://github.com/apache/fluss-rust/blob/main/rust-toolchain.toml) in the repo).
+
+```bash
+rustc --version
+cargo --version
+```
+
+To use `just release`, install [just](https://github.com/casey/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.
+
+### 2. Optional: Create a new Milestone in GitHub
+
+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.
+
+### 3. Optional: Triage release-blocking issues
+
+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.
+
+### 4. Clone fluss-rust into a fresh workspace
+
+Use a clean clone to avoid local changes affecting the release.
+
+```bash
+git clone https://github.com/apache/fluss-rust.git
+cd fluss-rust
+```
+
+### 5. Set up environment variables
+
+Set these once and use them in all following commands. (Bash syntax.)
+
+```bash
+export RELEASE_VERSION="0.1.0"
+export RELEASE_TAG="v${RELEASE_VERSION}"
+export SVN_RELEASE_DIR="fluss-rust-${RELEASE_VERSION}"
+# Only set if there is a previous release (for compare link in DISCUSS / release notes)
+export LAST_VERSION="0.0.9"
+export 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.
+
+### 6. Generate dependencies list
+
+[ASF release policy](https://www.apache.org/legal/release-policy.html) requires that every release comply with [ASF licensing policy](https://www.apache.org/legal/resolved.html) 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.
+
+1. Download and set up [cargo-deny](https://embarkstudios.github.io/cargo-deny/cli/index.html) (see cargo-deny docs).
+2. Run the script to update the dependency list, then commit on `main`:
+
+```bash
+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`.
+
+### 7. Optional: Start a [DISCUSS] thread
+
+On [Fluss Discussions](https://github.com/apache/fluss-rust/discussions) or the dev list:
+
+- **Subject:** `[DISCUSS] Release Apache Fluss clients (fluss-rust, fluss-python, fluss-cpp) $RELEASE_VERSION`
+- **Body:** Short rationale; if there is a previous release, add compare link: `https://github.com/apache/fluss-rust/compare/v${LAST_VERSION}...main`. Ask for comments.
+
+### 8. Create a release branch
+
+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.
+
+```bash
+git checkout main
+git pull
+git checkout -b release-${RELEASE_VERSION}
+git push origin release-${RELEASE_VERSION}
+```
+
+Do **not** create or push the release/RC tag yet; that happens in [Build a release candidate](#build-a-release-candidate) after the source artifacts are staged.
+
+### 9. Bump version on main for the next development cycle
+
+So that `main` moves to the next version immediately after the release branch is cut, run the bump script and commit:
+
+```bash
+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.
+
+### 10. Optional: Create PRs for release blog and download page
+
+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**
+
+- [ ] Rust (and optionally just) installed and on PATH
+- [ ] No release-blocking issues (or triaged)
+- [ ] Environment variables set
+- [ ] Release branch created and pushed
+- [ ] Main branch bumped to `NEXT_VERSION` and pushed
+- [ ] Dependencies list generated and committed on main
+- [ ] (Optional) DISCUSS thread and/or tracking issue created
+- [ ] (Optional) PRs for blog and download page created but not merged
+
+## Build a release candidate
+
+Each 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](#fix-any-issues)).
+
+### 1. Set RC environment variables
+
+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.
+
+```bash
+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.
+
+### 2. Check out the release branch and create the tag
+
+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).
+
+```bash
+git checkout release-${RELEASE_VERSION}
+git pull
+git tag -s $RC_TAG -m "${RC_TAG}"
+git push origin $RC_TAG
+```
+
+Check CI: [Actions](https://github.com/apache/fluss-rust/actions) (Release Rust, Release Python).
+
+### 3. Create source release artifacts
+
+From the repository root (on the release branch, at the commit you tagged):
+
+```bash
+just release $RELEASE_VERSION
+# Or: ./scripts/release.sh $RELEASE_VERSION
+```
+
+This creates under `dist/`:
+
+- `fluss-rust-${RELEASE_VERSION}.tar.gz`
+- `fluss-rust-${RELEASE_VERSION}.tar.gz.sha512`
+- `fluss-rust-${RELEASE_VERSION}.tar.gz.asc`
+
+Verify with: `gpg --verify dist/fluss-rust-${RELEASE_VERSION}.tar.gz.asc dist/fluss-rust-${RELEASE_VERSION}.tar.gz`
+
+### 4. Stage artifacts to SVN (dist.apache.org dev)
+
+From the **fluss-rust** repo root, check out the Fluss dev area and add the release artifacts.
+
+```bash
+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}.* $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/](https://dist.apache.org/repos/dist/dev/incubator/fluss/)
+
+---
+
+**Checklist to proceed to the next step**
+
+- [ ] Source distribution built and signed under `dist/`
+- [ ] Artifacts staged to [dist.apache.org dev](https://dist.apache.org/repos/dist/dev/incubator/fluss/) under `$SVN_RC_DIR`
+- [ ] RC (or release) tag pushed to GitHub
+- [ ] CI for Release Rust / Release Python succeeded
+
+## Vote on the release candidate
+
+Share the release candidate for community review. If the project is in incubation, a [two-phase vote](https://incubator.apache.org/cookbook/#two_phase_vote_on_podling_releases) (Fluss community then Incubator PMC) may be required; otherwise one community vote is enough.
+
+### Fluss community vote
+
+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 the project docs (todo: add how to verify release).
+
+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](#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.
+
+### Incubator PMC vote (if applicable)
+
+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**
+
+- [ ] Community vote passed (at least 3 binding +1, more +1 than -1)
+- [ ] (If incubating) Incubator PMC vote passed
+
+## Fix any issues
+
+If the vote revealed issues:
+
+1. Fix them on `main` (or the release branch) via normal PRs; cherry-pick fixes into the release branch as needed.
+2. Remove the old RC from dist.apache.org dev (optional but recommended):
+
+```bash
+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)"
+```
+
+3. Increment `RC_NUM` (e.g. set `RC_NUM="2"`), recreate `RC_TAG` and `SVN_RC_DIR`, then go back to [Build a release candidate](#build-a-release-candidate) and repeat until a candidate is approved.
+
+**Checklist**
+
+- [ ] Issues resolved and changes merged/cherry-picked to the release branch
+- [ ] New RC built and voted on (or same RC re-voted if only minor fixes)
+
+## Finalize the release
+
+Once a release candidate has been approved, finalize the release.
+
+### 1. Push the release git tag (if the vote was on an RC)
+
+If the community voted on an RC tag, create and push the formal release tag so CI publishes to crates.io and PyPI:
+
+```bash
+git checkout $RC_TAG
+git tag -s $RELEASE_TAG -m "Release fluss-rust, fluss-python, fluss-cpp ${RELEASE_VERSION}"
+git push origin $RELEASE_TAG
+```
+
+### 2. Deploy source artifacts to the release repository
+
+Move the staged artifacts from dev to release:
+
+```bash
+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.)
+
+### 3. Remove old RC(s) from dev (optional)
+
+Clean up the dev area so only the current RC or the moved release remains:
+
+```bash
+cd fluss-dist-dev
+svn remove $SVN_RC_DIR
+svn commit -m "Remove RC after release fluss-rust ${RELEASE_VERSION}"
+```
+
+### 4. Verify language artifacts
+
+- **fluss-rust:** [crates.io/crates/fluss-rs](https://crates.io/crates/fluss-rs) shows version `$RELEASE_VERSION`
+- **fluss-python:** [PyPI – pyfluss](https://pypi.org/project/pyfluss/) shows version `$RELEASE_VERSION`
+- **fluss-cpp:** Distributed via the source archive; no separate registry
+
+### 5. Create GitHub Release
+
+1. Go to [Releases → New release](https://github.com/apache/fluss-rust/releases/new).
+2. Choose tag `$RELEASE_TAG`.
+3. Set the target to the release branch `release-${RELEASE_VERSION}` (i.e., the branch/commit used to create `$RELEASE_TAG`).
+4. Click **Generate release notes**, then add: notable changes, breaking changes (if any) from component upgrade docs, **official download link** (source archive and verification), and install instructions for fluss-rust, fluss-python, fluss-cpp.
+ - **Download link:** `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.
+5. Click **Publish release**.
+
+### 6. Update CHANGELOG.md on main
+
+Add an entry for `$RELEASE_VERSION` with the list of changes (use [Generate Release Note](generate-release-note.md) from the release tag). Commit and push to `main`.
+
+---
+
+**Checklist to proceed to promotion**
+
+- [ ] Release tag pushed; CI published to crates.io and PyPI
+- [ ] Source artifacts in [dist release](https://dist.apache.org/repos/dist/release/incubator/fluss/)
+- [ ] GitHub Release created
+- [ ] CHANGELOG.md updated on main
+
+## Promote the release
+
+### Merge website PRs
+
+Merge the pull requests for the release blog and download page that were created in [Prepare for the release](#9-optional-create-prs-for-release-blog-and-download-page).
Review Comment:
+1
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@fluss.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] build: add how to release doc [fluss-rust]
Posted by "fresh-borzoni (via GitHub)" <gi...@apache.org>.
fresh-borzoni commented on code in PR #268:
URL: https://github.com/apache/fluss-rust/pull/268#discussion_r2777499215
##########
.github/workflows/release_python.yml:
##########
@@ -0,0 +1,152 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Publish the fluss Python binding to PyPI.
+# Trigger: push tag only (e.g. v0.1.0).
+# Pre-release tags (containing '-') publish to TestPyPI; release tags publish to PyPI.
+#
+# Token auth: set repo variable PYPI_USE_TOKEN_AUTH = 'true' and add secrets PYPI_API_TOKEN / TEST_PYPI_API_TOKEN.
+# Trusted Publishing (OIDC): leave PYPI_USE_TOKEN_AUTH unset; do not pass password so the action uses OIDC.
+
+name: Release Python
+
+on:
+ push:
+ tags:
+ - "*"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+
+jobs:
+ sdist:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install protoc
+ run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
+
+ - uses: PyO3/maturin-action@v1
+ with:
+ working-directory: bindings/python
+ command: sdist
+ args: -o dist
+
+ - name: Upload sdist
+ uses: actions/upload-artifact@v4
+ with:
+ name: wheels-sdist
+ path: bindings/python/dist
+
+ wheels:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ include:
+ - { os: windows-latest }
+ - { os: macos-15-intel, target: "x86_64-apple-darwin" }
+ - { os: macos-15, target: "aarch64-apple-darwin" }
+ - { os: ubuntu-latest, target: "x86_64" }
+ - { os: ubuntu-latest, target: "aarch64", manylinux: "manylinux_2_28" }
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install protoc (Linux)
+ if: runner.os == 'Linux'
+ run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
+
+ - name: Install protoc (macOS)
+ if: runner.os == 'macOS'
+ run: brew install protobuf
+
+ - name: Install protoc (Windows)
+ if: runner.os == 'Windows'
+ run: choco install protobuf -y
+ shell: pwsh
+
+ - uses: PyO3/maturin-action@v1
+ with:
+ working-directory: bindings/python
+ target: ${{ matrix.target }}
+ command: build
+ args: --release -o dist -i python3.11
+ manylinux: ${{ matrix.manylinux || 'auto' }}
+ - uses: PyO3/maturin-action@v1
+ with:
+ working-directory: bindings/python
+ target: ${{ matrix.target }}
+ command: build
+ args: --release -o dist -i python3.12
+ manylinux: ${{ matrix.manylinux || 'auto' }}
Review Comment:
+1
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@fluss.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] build: add how to release doc [fluss-rust]
Posted by "luoyuxia (via GitHub)" <gi...@apache.org>.
luoyuxia commented on PR #268:
URL: https://github.com/apache/fluss-rust/pull/268#issuecomment-3864568627
@fresh-borzoni Thanks for your review. Comments addressed.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@fluss.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] build: add how to release doc [fluss-rust]
Posted by "luoyuxia (via GitHub)" <gi...@apache.org>.
luoyuxia commented on code in PR #268:
URL: https://github.com/apache/fluss-rust/pull/268#discussion_r2777428655
##########
crates/fluss/Cargo.toml:
##########
@@ -20,6 +20,14 @@ edition = { workspace = true }
license.workspace = true
rust-version = { workspace = true }
version = { workspace = true }
+name = "fluss-rs"
Review Comment:
it's pitty that `fluss` has been occupied in crate. So, we have to use another name.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@fluss.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] build: add how to release doc [fluss-rust]
Posted by "luoyuxia (via GitHub)" <gi...@apache.org>.
luoyuxia commented on PR #268:
URL: https://github.com/apache/fluss-rust/pull/268#issuecomment-3864181894
@fresh-borzoni @leekeiabstraction @zhaohaidao Could you please help review this?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@fluss.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] build: add how to release doc [fluss-rust]
Posted by "luoyuxia (via GitHub)" <gi...@apache.org>.
luoyuxia commented on PR #268:
URL: https://github.com/apache/fluss-rust/pull/268#issuecomment-3865870511
will merge it and then to improve if has any
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@fluss.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] build: add how to release doc [fluss-rust]
Posted by "luoyuxia (via GitHub)" <gi...@apache.org>.
luoyuxia merged PR #268:
URL: https://github.com/apache/fluss-rust/pull/268
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@fluss.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] build: add how to release doc [fluss-rust]
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #268:
URL: https://github.com/apache/fluss-rust/pull/268#discussion_r2777427459
##########
crates/examples/Cargo.toml:
##########
@@ -24,7 +24,7 @@ version = { workspace = true }
[dependencies]
-fluss = { workspace = true }
+fluss-rs = { workspace = true }
Review Comment:
This crate now depends on `fluss-rs`, but the example sources import `use fluss::...` (see `crates/examples/src/*`). With the dependency key set to `fluss-rs`, Cargo will expose it as `fluss_rs`, so these examples will not compile. Consider depending as `fluss = { package = "fluss-rs", workspace = true }` (or update all imports to `fluss_rs`).
```suggestion
fluss = { package = "fluss-rs", workspace = true }
```
##########
Cargo.toml:
##########
@@ -16,24 +16,26 @@
# under the License.
[workspace.package]
-categories = ["command-line-utilities"]
+authors = ["Apache Fluss <de...@fluss.apache.org>"]
+categories = ["api-bindings", "database"]
description = "The rust implementation of fluss"
-repository = "https://github.com/apache/fluss-rust"
edition = "2024"
-version = "0.1.0"
+homepage = "https://fluss.apache.org/"
license = "Apache-2.0"
+repository = "https://github.com/apache/fluss-rust"
rust-version = "1.85"
+version = "0.1.0"
+keywords = ["fluss", "streaming-storage", "datalake"]
[workspace]
resolver = "2"
members = ["crates/fluss", "crates/examples", "bindings/python", "bindings/cpp"]
[workspace.dependencies]
-fluss = { version = "0.1.0", path = "./crates/fluss" }
+fluss-rs = { version = "0.1.0", path = "./crates/fluss" }
Review Comment:
Workspace dependency is defined as `fluss-rs`, but the crate being built exports `lib` as `fluss` and downstream code imports `use fluss::...` (e.g. examples/bindings). This mismatch will cause compilation failures; keep the dependency key as `fluss` and set `package = "fluss-rs"` (or rename the library crate/imports consistently).
```suggestion
fluss = { package = "fluss-rs", version = "0.1.0", path = "./crates/fluss" }
```
##########
bindings/python/Cargo.toml:
##########
@@ -17,18 +17,18 @@
[package]
name = "fluss_python"
-edition = "2024"
-version = "0.1.0"
+edition.workspace = true
+version.workspace = true
license.workspace = true
-rust-version = "1.85"
+rust-version.workspace = true
[lib]
name = "fluss"
crate-type = ["cdylib"]
[dependencies]
pyo3 = { version = "0.26.0", features = ["extension-module"] }
-fluss = { path = "../../crates/fluss" }
+fluss-rs = { workspace = true, features = ["storage-all"] }
Review Comment:
The bindings code imports `use fluss::...`, but this dependency is declared as `fluss-rs`, which will be available to Rust code as `fluss_rs`. This will break the build unless the dependency is aliased (e.g. `fluss = { package = "fluss-rs", workspace = true, ... }`) or the code is updated to import `fluss_rs`.
```suggestion
fluss = { package = "fluss-rs", workspace = true, features = ["storage-all"] }
```
##########
scripts/dependencies.py:
##########
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
+from pathlib import Path
+import subprocess
+
+from constants import PACKAGES, ROOT_DIR
+
+
+def check_single_package(root):
+ pkg_dir = ROOT_DIR / root if root != "." else ROOT_DIR
+ if (pkg_dir / "Cargo.toml").exists():
+ print(f"Checking dependencies of {root}")
+ subprocess.run(["cargo", "deny", "check", "license"], cwd=pkg_dir)
Review Comment:
`cargo deny check` failures won’t fail this script because `subprocess.run(...)` isn’t checked. Use `check=True` or explicitly inspect `returncode` and exit non-zero so CI/release checks don’t silently pass when license checks fail.
```suggestion
subprocess.run(
["cargo", "deny", "check", "license"],
cwd=pkg_dir,
check=True,
)
```
##########
README.md:
##########
@@ -132,6 +132,10 @@ Then, stop your Fluss cluster. Go to your Fluss home, stop it via the following
./bin/local-cluster.sh stop
```
+## Documentation
+
+- [Development Guide](DEVELOPMENT.md) – Build, test, and contribute to fluss-rust.
+- [Release Guide](RELEASE.md) – How to build, release, and sign official Fluss client packages (Rust, Python, C++), and how to **verify** signed releases.
Review Comment:
`RELEASE.md` is linked here but the repository root doesn’t contain a `RELEASE.md` file. Either add the file (and keep it in sync) or link to the new release documentation under `docs/` (e.g. `docs/creating-a-release.md`).
```suggestion
- [Release Guide](docs/creating-a-release.md) – How to build, release, and sign official Fluss client packages (Rust, Python, C++), and how to **verify** signed releases.
```
##########
scripts/release.sh:
##########
@@ -0,0 +1,66 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Create ASF source release artifacts under dist/:
+# fluss-rust-{version}.tar.gz
+# fluss-rust-{version}.tar.gz.asc
+# fluss-rust-{version}.tar.gz.sha512
+# Run from repo root. Check out the release tag first (e.g. git checkout v0.1.0-rc.1).
+# Usage: ./scripts/release.sh [version]
+# If version is omitted, it is read from Cargo.toml (workspace.package.version).
+
+set -e
+
+REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+cd "$REPO_ROOT"
+
+if [ -n "$1" ]; then
+ VERSION="$1"
+else
+ VERSION=$(grep -E '^version\s*=' Cargo.toml | head -1 | sed 's/.*"\([^"]*\)".*/\1/')
+ if [ -z "$VERSION" ]; then
+ echo "Could not read version from Cargo.toml. Pass version as argument: $0 <version>"
+ exit 1
+ fi
+fi
+
+PREFIX="fluss-rust-${VERSION}"
+DIST_DIR="${REPO_ROOT}/dist"
+TARBALL="${PREFIX}.tar.gz"
+
+echo "Creating ASF source release for fluss-rust ${VERSION}"
+mkdir -p "$DIST_DIR"
+
+echo "Creating source archive: ${TARBALL}"
+git archive --format=tar.gz --prefix="${PREFIX}/" -o "${DIST_DIR}/${TARBALL}" HEAD
+
+echo "Generating SHA-512 checksum: ${TARBALL}.sha512"
+if command -v shasum >/dev/null 2>&1; then
+ (cd "$DIST_DIR" && shasum -a 512 "$TARBALL" > "${TARBALL}.sha512")
+else
+ (cd "$DIST_DIR" && sha512sum "$TARBALL" > "${TARBALL}.sha512")
+fi
+
+echo "Signing with GPG: ${TARBALL}.asc"
+(cd "$DIST_DIR" && gpg --armor --detach-sig "$TARBALL")
+
+echo "Verifying signature"
+(cd "$DIST_DIR" && gpg --verify "${TARBALL}.asc" "$TARBALL")
+
+echo "Done. Artifacts in dist/:"
+ls -la "${DIST_DIR}/"
+echo ""
+echo "Next: upload contents of dist/ to SVN (see HOW_TO_RELEASE.md §9.2)."
Review Comment:
The script suggests uploading to SVN per `HOW_TO_RELEASE.md §9.2`, but there is no `HOW_TO_RELEASE.md` in this repo. Update the pointer to an existing document (e.g. `docs/creating-a-release.md`) so users can follow the next step.
```suggestion
echo "Next: upload contents of dist/ to SVN (see docs/creating-a-release.md)."
```
##########
crates/fluss/Cargo.toml:
##########
@@ -20,6 +20,14 @@ edition = { workspace = true }
license.workspace = true
rust-version = { workspace = true }
version = { workspace = true }
+name = "fluss-rs"
+authors = { workspace = true }
+description = { workspace = true }
+homepage = { workspace = true }
+repository = { workspace = true }
+keywords = { workspace = true }
+
+[lib]
name = "fluss"
Review Comment:
`[package].name` was changed to `fluss-rs` while `[lib].name` remains `fluss`. That’s fine for publishing, but it requires all dependents to alias the dependency name to `fluss` (e.g. `fluss = { package = "fluss-rs", ... }`). As-is, other Cargo.toml files in this PR depend on `fluss-rs` directly, which won’t match the library crate name and will break compilation.
```suggestion
name = "fluss-rs"
```
##########
scripts/bump-version.sh:
##########
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Bump version in root Cargo.toml ([workspace.package] and [workspace.dependencies] fluss-rs).
+# Run from repo root. Use after cutting a release branch so main is set to the next version.
+#
+# Usage: ./scripts/bump-version.sh <current_version> <next_version>
+# e.g. ./scripts/bump-version.sh 0.1.0 0.1.1
+# Or with env vars: ./scripts/bump-version.sh $RELEASE_VERSION $NEXT_VERSION
+
+set -e
+
+if [ -z "$1" ] || [ -z "$2" ]; then
+ echo "Usage: $0 <current_version> <next_version>"
+ echo " e.g. $0 0.1.0 0.1.1"
+ exit 1
+fi
+
+FROM_VERSION="$1"
+TO_VERSION="$2"
+
+REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+cd "$REPO_ROOT"
+
+if [ ! -f Cargo.toml ]; then
+ echo "Cargo.toml not found. Run from repo root."
+ exit 1
+fi
+
+# Replace version = "X.Y.Z" with version = "TO_VERSION" (all occurrences in root Cargo.toml)
+case "$(uname -s)" in
+ Darwin)
+ sed -i '' "s/version = \"${FROM_VERSION}\"/version = \"${TO_VERSION}\"/g" Cargo.toml
+ ;;
+ *)
+ sed -i "s/version = \"${FROM_VERSION}\"/version = \"${TO_VERSION}\"/g" Cargo.toml
+ ;;
+esac
Review Comment:
The sed replacement treats `FROM_VERSION` as a regex, so dots in versions (e.g. `0.1.0`) match any character and could replace unintended strings. Consider escaping the version before passing it to sed and/or restricting replacements to the specific keys that should change (workspace.package.version and workspace.dependencies fluss-rs) instead of a global substitution.
##########
scripts/dependencies.py:
##########
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
+from pathlib import Path
Review Comment:
Import of 'Path' is not used.
```suggestion
```
##########
docs/creating-a-release.md:
##########
@@ -0,0 +1,453 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+# Creating a Fluss Rust Client Release
+
+This document describes in detail how to create a release of the **Fluss clients** (fluss-rust, fluss-python, fluss-cpp) from the [fluss-rust](https://github.com/apache/fluss-rust) repository. It is based on the [Creating a Fluss Release](https://fluss.apache.org/community/how-to-release/creating-a-fluss-release/) guide of the Apache Fluss project and the [release guide of Apache OpenDAL](https://nightlies.apache.org/opendal/opendal-docs-stable/community/release/); 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](https://www.apache.org/legal/release-policy.html) and [Release Distribution Policy](https://infra.apache.org/release-distribution.html).
+
+## Overview
+
+
+
+The release process consists of:
+
+1. [Decide to release](#decide-to-release)
+2. [Prepare for the release](#prepare-for-the-release)
+3. [Build a release candidate](#build-a-release-candidate)
+4. [Vote on the release candidate](#vote-on-the-release-candidate)
+5. [If necessary, fix any issues and go back to step 3](#fix-any-issues)
+6. [Finalize the release](#finalize-the-release)
+7. [Promote the release](#promote-the-release)
+
+## Decide to release
+
+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**
+
+- [ ] Community agrees to release
+- [ ] A Release Manager is selected
+
+## Prepare for the release
+
+### 0. One-time Release Manager setup
+
+Before your first release, perform one-time configuration. See **[Release Manager Preparation](https://fluss.apache.org/community/how-to-release/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)**
+
+- [ ] GPG key set up and published to [KEYS](https://downloads.apache.org/fluss/KEYS) or Apache account
+- [ ] Git configured to use your GPG key for signing tags
+
+### 1. Install Rust (and optional: just)
+
+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](https://rustup.rs/) (the version should match [rust-toolchain.toml](https://github.com/apache/fluss-rust/blob/main/rust-toolchain.toml) in the repo).
+
+```bash
+rustc --version
+cargo --version
+```
+
+To use `just release`, install [just](https://github.com/casey/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.
+
+### 2. Optional: Create a new Milestone in GitHub
+
+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.
+
+### 3. Optional: Triage release-blocking issues
+
+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.
+
+### 4. Clone fluss-rust into a fresh workspace
+
+Use a clean clone to avoid local changes affecting the release.
+
+```bash
+git clone https://github.com/apache/fluss-rust.git
+cd fluss-rust
+```
+
+### 5. Set up environment variables
+
+Set these once and use them in all following commands. (Bash syntax.)
+
+```bash
+export RELEASE_VERSION="0.1.0"
+export RELEASE_TAG="v${RELEASE_VERSION}"
+export SVN_RELEASE_DIR="fluss-rust-${RELEASE_VERSION}"
+# Only set if there is a previous release (for compare link in DISCUSS / release notes)
+export LAST_VERSION="0.0.9"
+export 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.
+
+### 6. Generate dependencies list
+
+[ASF release policy](https://www.apache.org/legal/release-policy.html) requires that every release comply with [ASF licensing policy](https://www.apache.org/legal/resolved.html) 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.
+
+1. Download and set up [cargo-deny](https://embarkstudios.github.io/cargo-deny/cli/index.html) (see cargo-deny docs).
+2. Run the script to update the dependency list, then commit on `main`:
+
+```bash
+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`.
+
+### 7. Optional: Start a [DISCUSS] thread
+
+On [Fluss Discussions](https://github.com/apache/fluss-rust/discussions) or the dev list:
+
+- **Subject:** `[DISCUSS] Release Apache Fluss clients (fluss-rust, fluss-python, fluss-cpp) $RELEASE_VERSION`
+- **Body:** Short rationale; if there is a previous release, add compare link: `https://github.com/apache/fluss-rust/compare/v${LAST_VERSION}...main`. Ask for comments.
+
+### 8. Create a release branch
+
+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.
+
+```bash
+git checkout main
+git pull
+git checkout -b release-${RELEASE_VERSION}
+git push origin release-${RELEASE_VERSION}
+```
+
+Do **not** create or push the release/RC tag yet; that happens in [Build a release candidate](#build-a-release-candidate) after the source artifacts are staged.
+
+### 9. Bump version on main for the next development cycle
+
+So that `main` moves to the next version immediately after the release branch is cut, run the bump script and commit:
+
+```bash
+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.
+
+### 10. Optional: Create PRs for release blog and download page
+
+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**
+
+- [ ] Rust (and optionally just) installed and on PATH
+- [ ] No release-blocking issues (or triaged)
+- [ ] Environment variables set
+- [ ] Release branch created and pushed
+- [ ] Main branch bumped to `NEXT_VERSION` and pushed
+- [ ] Dependencies list generated and committed on main
+- [ ] (Optional) DISCUSS thread and/or tracking issue created
+- [ ] (Optional) PRs for blog and download page created but not merged
+
+## Build a release candidate
+
+Each 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](#fix-any-issues)).
+
+### 1. Set RC environment variables
+
+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.
+
+```bash
+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.
+
+### 2. Check out the release branch and create the tag
+
+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).
+
+```bash
+git checkout release-${RELEASE_VERSION}
+git pull
+git tag -s $RC_TAG -m "${RC_TAG}"
+git push origin $RC_TAG
+```
+
+Check CI: [Actions](https://github.com/apache/fluss-rust/actions) (Release Rust, Release Python).
+
+### 3. Create source release artifacts
+
+From the repository root (on the release branch, at the commit you tagged):
+
+```bash
+just release $RELEASE_VERSION
+# Or: ./scripts/release.sh $RELEASE_VERSION
+```
+
+This creates under `dist/`:
+
+- `fluss-rust-${RELEASE_VERSION}.tar.gz`
+- `fluss-rust-${RELEASE_VERSION}.tar.gz.sha512`
+- `fluss-rust-${RELEASE_VERSION}.tar.gz.asc`
+
+Verify with: `gpg --verify dist/fluss-rust-${RELEASE_VERSION}.tar.gz.asc dist/fluss-rust-${RELEASE_VERSION}.tar.gz`
+
+### 4. Stage artifacts to SVN (dist.apache.org dev)
+
+From the **fluss-rust** repo root, check out the Fluss dev area and add the release artifacts.
+
+```bash
+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}.* $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/](https://dist.apache.org/repos/dist/dev/incubator/fluss/)
+
+---
+
+**Checklist to proceed to the next step**
+
+- [ ] Source distribution built and signed under `dist/`
+- [ ] Artifacts staged to [dist.apache.org dev](https://dist.apache.org/repos/dist/dev/incubator/fluss/) under `$SVN_RC_DIR`
+- [ ] RC (or release) tag pushed to GitHub
+- [ ] CI for Release Rust / Release Python succeeded
+
+## Vote on the release candidate
+
+Share the release candidate for community review. If the project is in incubation, a [two-phase vote](https://incubator.apache.org/cookbook/#two_phase_vote_on_podling_releases) (Fluss community then Incubator PMC) may be required; otherwise one community vote is enough.
+
+### Fluss community vote
+
+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/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 the project docs (HOW_TO_RELEASE.md / RELEASE.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](#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.
+
+### Incubator PMC vote (if applicable)
+
+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**
+
+- [ ] Community vote passed (at least 3 binding +1, more +1 than -1)
+- [ ] (If incubating) Incubator PMC vote passed
+
+## Fix any issues
+
+If the vote revealed issues:
+
+1. Fix them on `main` (or the release branch) via normal PRs; cherry-pick fixes into the release branch as needed.
+2. Remove the old RC from dist.apache.org dev (optional but recommended):
+
+```bash
+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)"
+```
+
+3. Increment `RC_NUM` (e.g. set `RC_NUM="2"`), recreate `RC_TAG` and `SVN_RC_DIR`, then go back to [Build a release candidate](#build-a-release-candidate) and repeat until a candidate is approved.
+
+**Checklist**
+
+- [ ] Issues resolved and changes merged/cherry-picked to the release branch
+- [ ] New RC built and voted on (or same RC re-voted if only minor fixes)
+
+## Finalize the release
+
+Once a release candidate has been approved, finalize the release.
+
+### 1. Push the release git tag (if the vote was on an RC)
+
+If the community voted on an RC tag, create and push the formal release tag so CI publishes to crates.io and PyPI:
+
+```bash
+git checkout $RC_TAG
+git tag -s $RELEASE_TAG -m "Release fluss-rust, fluss-python, fluss-cpp ${RELEASE_VERSION}"
+git push origin $RELEASE_TAG
+```
+
+### 2. Deploy source artifacts to the release repository
+
+Move the staged artifacts from dev to release:
+
+```bash
+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.)
+
+### 3. Remove old RC(s) from dev (optional)
+
+Clean up the dev area so only the current RC or the moved release remains:
+
+```bash
+cd fluss-dist-dev
+svn remove $SVN_RC_DIR
+svn commit -m "Remove RC after release fluss-rust ${RELEASE_VERSION}"
+```
+
+### 4. Verify language artifacts
+
+- **fluss-rust:** [crates.io/crates/fluss-rs](https://crates.io/crates/fluss-rs) shows version `$RELEASE_VERSION`
+- **fluss-python:** [PyPI – pyfluss](https://pypi.org/project/pyfluss/) shows version `$RELEASE_VERSION`
+- **fluss-cpp:** Distributed via the source archive; no separate registry
+
+### 5. Create GitHub Release
+
+1. Go to [Releases → New release](https://github.com/apache/fluss-rust/releases/new).
+2. Choose tag `$RELEASE_TAG`.
+3. Set target branch to `main`.
+4. Click **Generate release notes**, then add: notable changes, breaking changes (if any) from component upgrade docs, **official download link** (source archive and verification), and install instructions for fluss-rust, fluss-python, fluss-cpp.
+ - **Download link:** `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.
+5. Click **Publish release**.
+
+### 6. Update CHANGELOG.md on main
+
+Add an entry for `$RELEASE_VERSION` with the list of changes (use [Generate Release Note](generate-release-note.md) from the release tag). Commit and push to `main`.
+
+---
+
+**Checklist to proceed to promotion**
+
+- [ ] Release tag pushed; CI published to crates.io and PyPI
+- [ ] Source artifacts in [dist release](https://dist.apache.org/repos/dist/release/incubator/fluss/)
+- [ ] GitHub Release created
+- [ ] CHANGELOG.md updated on main
+
+## Promote the release
+
+### Merge website PRs
+
+Merge the pull requests for the release blog and download page that were created in [Prepare for the release](#9-optional-create-prs-for-release-blog-and-download-page).
+
+### Announce the release
+
+Wait at least 24 hours after finalizing, per [ASF release policy](https://www.apache.org/legal/release-policy.html#release-announcements).
+
+- Announce on the dev mailing list that the release is complete.
+- Announce on [Fluss Discussions – Announcements](https://github.com/apache/fluss-rust/discussions) (if that category exists).
+- Send the release announcement to **announce@apache.org**.
+
+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
+```
Review Comment:
This document links to `../HOW_TO_RELEASE.md` and mentions `RELEASE.md`, but neither file exists in the repository root. Please either add those files or update these references to point at the actual release docs (e.g. `docs/creating-a-release.md`).
##########
docs/generate-release-note.md:
##########
@@ -0,0 +1,28 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
Review Comment:
This link points to `creating-a-fluss-rust-release.md`, but the docs directory contains `creating-a-release.md` instead. Update the link target (or rename the file) so the reference doesn’t 404.
##########
bindings/cpp/Cargo.toml:
##########
@@ -30,8 +30,8 @@ crate-type = ["staticlib"]
anyhow = "1.0"
arrow = { workspace = true, features = ["ffi"] }
cxx = "1.0"
-fluss = { path = "../../crates/fluss" }
-tokio = { version = "1.27", features = ["rt-multi-thread", "macros"] }
+fluss-rs = { workspace = true, features = ["storage-all"] }
Review Comment:
The C++ bindings sources import `use fluss::...`, but this dependency is declared as `fluss-rs` (which Cargo exposes as `fluss_rs`). This mismatch will prevent compilation unless the dependency is aliased to `fluss` (with `package = "fluss-rs"`) or the Rust imports are updated accordingly.
```suggestion
fluss = { package = "fluss-rs", workspace = true, features = ["storage-all"] }
```
##########
.github/workflows/release_rust.yml:
##########
@@ -0,0 +1,57 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Publish the fluss Rust crate to crates.io.
+# Trigger: push tag only (e.g. v0.1.0).
+# Pre-release tags (containing '-') do not publish; release tags publish to crates.io.
+#
+# First publish: add repo secret CARGO_REGISTRY_TOKEN (crates.io API token); CI will use it.
+# After crate exists: set up Trusted Publishing on the crate page, then remove the secret; CI will use OIDC.
+
+name: Release Rust
+
+on:
+ push:
+ tags:
+ - "*"
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ permissions:
+ id-token: write
+ steps:
+ - uses: actions/checkout@v4
+
Review Comment:
This job sets `permissions` to only `id-token: write`, which removes the default `contents: read` permission. `actions/checkout` typically requires `contents: read`, so the workflow may fail to fetch the repo. Add `contents: read` (and keep `id-token: write`) at the job or workflow permissions level.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@fluss.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] build: add how to release doc [fluss-rust]
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #268:
URL: https://github.com/apache/fluss-rust/pull/268#discussion_r2777460132
##########
scripts/dependencies.py:
##########
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import subprocess
+from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
+
+from constants import PACKAGES, ROOT_DIR
+
+
+def check_single_package(root):
+ pkg_dir = ROOT_DIR / root if root != "." else ROOT_DIR
+ if (pkg_dir / "Cargo.toml").exists():
+ print(f"Checking dependencies of {root}")
+ subprocess.run(
+ ["cargo", "deny", "check", "license"],
+ cwd=pkg_dir,
+ check=True,
Review Comment:
The arguments inside `subprocess.run(...)` in `check_single_package` are indented differently than the rest of the file (compare with `generate_single_package`). Re-indent these lines for readability/consistency.
```suggestion
["cargo", "deny", "check", "license"],
cwd=pkg_dir,
check=True,
```
##########
.github/workflows/release_python.yml:
##########
@@ -0,0 +1,137 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Publish the fluss Python binding to PyPI.
+# Trigger: push tag only (e.g. v0.1.0).
+# Pre-release tags (containing '-') publish to TestPyPI; release tags publish to PyPI.
+#
+# First publish: add repo secrets PYPI_API_TOKEN and/or TEST_PYPI_API_TOKEN (from pypi.org / test.pypi.org);
+# CI will use them. After Trusted Publishing is set up on PyPI/TestPyPI, you can remove the secrets.
+
+name: Release Python
+
+on:
+ push:
+ tags:
+ - "*"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+
+jobs:
+ sdist:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install protoc
+ run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
+
+ - uses: PyO3/maturin-action@v1
+ with:
+ working-directory: bindings/python
+ command: sdist
+ args: -o dist
+
+ - name: Upload sdist
+ uses: actions/upload-artifact@v4
+ with:
+ name: wheels-sdist
+ path: bindings/python/dist
+
+ wheels:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ include:
+ - { os: windows-latest }
+ - { os: macos-15-intel, target: "x86_64-apple-darwin" }
+ - { os: macos-15, target: "aarch64-apple-darwin" }
+ - { os: ubuntu-latest, target: "x86_64" }
+ - { os: ubuntu-latest, target: "aarch64", manylinux: "manylinux_2_28" }
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install protoc (Linux)
+ if: runner.os == 'Linux'
+ run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
+
+ - name: Install protoc (macOS)
+ if: runner.os == 'macOS'
+ run: brew install protobuf
+
+ - name: Install protoc (Windows)
+ if: runner.os == 'Windows'
+ run: choco install protobuf -y
+ shell: pwsh
+
+ - uses: PyO3/maturin-action@v1
+ with:
+ working-directory: bindings/python
+ target: ${{ matrix.target }}
+ command: build
+ args: --release -o dist -i python3.11
+ manylinux: ${{ matrix.manylinux || 'auto' }}
+ - uses: PyO3/maturin-action@v1
+ with:
+ working-directory: bindings/python
+ target: ${{ matrix.target }}
+ command: build
+ args: --release -o dist -i python3.12
+ manylinux: ${{ matrix.manylinux || 'auto' }}
+
+ - name: Upload wheels
+ uses: actions/upload-artifact@v4
+ with:
+ name: wheels-${{ matrix.os }}-${{ matrix.target || 'native' }}
+ path: bindings/python/dist
+
+ release:
+ name: Publish to PyPI
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ needs: [sdist, wheels]
+ if: startsWith(github.ref, 'refs/tags/')
+ steps:
+ - uses: actions/download-artifact@v4
+ with:
+ pattern: wheels-*
+ merge-multiple: true
+ path: bindings/python/dist
+
+ - name: Publish to TestPyPI (pre-release tags)
+ if: contains(github.ref, '-')
+ uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ repository-url: https://test.pypi.org/legacy/
+ skip-existing: true
+ packages-dir: bindings/python/dist
+ password: ${{ secrets.TEST_PYPI_API_TOKEN }}
+
Review Comment:
The comments indicate secrets can be removed after setting up Trusted Publishing, but this step always passes `password: ${{ secrets.TEST_PYPI_API_TOKEN }}`. If the secret is removed, the action will still receive an empty password and OIDC mode won’t be used. Add a separate step (or conditional) that omits `password` when the secret is not set so Trusted Publishing actually works.
##########
scripts/release.sh:
##########
@@ -0,0 +1,66 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Create ASF source release artifacts under dist/:
+# fluss-rust-{version}.tar.gz
+# fluss-rust-{version}.tar.gz.asc
+# fluss-rust-{version}.tar.gz.sha512
+# Run from repo root. Check out the release tag first (e.g. git checkout v0.1.0-rc.1).
Review Comment:
The example RC tag format here (`v0.1.0-rc.1`) conflicts with the release guide which uses `v${RELEASE_VERSION}-rc${RC_NUM}` (no dot). Pick one tag naming scheme and use it consistently across scripts/docs, since CI logic and user instructions depend on the exact tag name.
```suggestion
# Run from repo root. Check out the release tag first (e.g. git checkout v0.1.0-rc1).
```
##########
.github/workflows/release_rust.yml:
##########
@@ -0,0 +1,58 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Publish the fluss Rust crate to crates.io.
+# Trigger: push tag only (e.g. v0.1.0).
+# Pre-release tags (containing '-') do not publish; release tags publish to crates.io.
+#
+# First publish: add repo secret CARGO_REGISTRY_TOKEN (crates.io API token); CI will use it.
+# After crate exists: set up Trusted Publishing on the crate page, then remove the secret; CI will use OIDC.
+
+name: Release Rust
+
+on:
+ push:
+ tags:
+ - "*"
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install protoc
+ run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
+
+ - name: Dry run (crates/fluss)
+ run: cargo publish -p fluss-rs --dry-run
+
+ - name: Get crates.io token (OIDC)
+ if: secrets.CARGO_REGISTRY_TOKEN == ''
+ uses: rust-lang/crates-io-auth-action@v1
+ id: auth
+ with:
+ token-type: publish
+
Review Comment:
This OIDC auth step will run on *all* tag pushes when `CARGO_REGISTRY_TOKEN` is not set, including RC/pre-release tags where publishing is intentionally skipped. If trusted publishing is configured to only allow release tags, this step can fail and break RC workflows. Consider gating this step with the same condition as the publish step (e.g., `!contains(github.ref, '-')`).
##########
docs/creating-a-release.md:
##########
@@ -0,0 +1,452 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+# Creating a Fluss Rust Client Release
+
+This document describes in detail how to create a release of the **Fluss clients** (fluss-rust, fluss-python, fluss-cpp) from the [fluss-rust](https://github.com/apache/fluss-rust) repository. It is based on the [Creating a Fluss Release](https://fluss.apache.org/community/how-to-release/creating-a-fluss-release/) guide of the Apache Fluss project and the [release guide of Apache OpenDAL](https://nightlies.apache.org/opendal/opendal-docs-stable/community/release/); 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](https://www.apache.org/legal/release-policy.html) and [Release Distribution Policy](https://infra.apache.org/release-distribution.html).
+
+## Overview
+
+
+
+The release process consists of:
+
+1. [Decide to release](#decide-to-release)
+2. [Prepare for the release](#prepare-for-the-release)
+3. [Build a release candidate](#build-a-release-candidate)
+4. [Vote on the release candidate](#vote-on-the-release-candidate)
+5. [If necessary, fix any issues and go back to step 3](#fix-any-issues)
+6. [Finalize the release](#finalize-the-release)
+7. [Promote the release](#promote-the-release)
+
+## Decide to release
+
+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**
+
+- [ ] Community agrees to release
+- [ ] A Release Manager is selected
+
+## Prepare for the release
+
+### 0. One-time Release Manager setup
+
+Before your first release, perform one-time configuration. See **[Release Manager Preparation](https://fluss.apache.org/community/how-to-release/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)**
+
+- [ ] GPG key set up and published to [KEYS](https://downloads.apache.org/fluss/KEYS) or Apache account
+- [ ] Git configured to use your GPG key for signing tags
+
+### 1. Install Rust (and optional: just)
+
+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](https://rustup.rs/) (the version should match [rust-toolchain.toml](https://github.com/apache/fluss-rust/blob/main/rust-toolchain.toml) in the repo).
+
+```bash
+rustc --version
+cargo --version
+```
+
+To use `just release`, install [just](https://github.com/casey/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.
+
+### 2. Optional: Create a new Milestone in GitHub
+
+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.
+
+### 3. Optional: Triage release-blocking issues
+
+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.
+
+### 4. Clone fluss-rust into a fresh workspace
+
+Use a clean clone to avoid local changes affecting the release.
+
+```bash
+git clone https://github.com/apache/fluss-rust.git
+cd fluss-rust
+```
+
+### 5. Set up environment variables
+
+Set these once and use them in all following commands. (Bash syntax.)
+
+```bash
+export RELEASE_VERSION="0.1.0"
+export RELEASE_TAG="v${RELEASE_VERSION}"
+export SVN_RELEASE_DIR="fluss-rust-${RELEASE_VERSION}"
+# Only set if there is a previous release (for compare link in DISCUSS / release notes)
+export LAST_VERSION="0.0.9"
+export 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.
+
+### 6. Generate dependencies list
+
+[ASF release policy](https://www.apache.org/legal/release-policy.html) requires that every release comply with [ASF licensing policy](https://www.apache.org/legal/resolved.html) 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.
+
+1. Download and set up [cargo-deny](https://embarkstudios.github.io/cargo-deny/cli/index.html) (see cargo-deny docs).
+2. Run the script to update the dependency list, then commit on `main`:
+
+```bash
+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`.
+
+### 7. Optional: Start a [DISCUSS] thread
+
+On [Fluss Discussions](https://github.com/apache/fluss-rust/discussions) or the dev list:
+
+- **Subject:** `[DISCUSS] Release Apache Fluss clients (fluss-rust, fluss-python, fluss-cpp) $RELEASE_VERSION`
+- **Body:** Short rationale; if there is a previous release, add compare link: `https://github.com/apache/fluss-rust/compare/v${LAST_VERSION}...main`. Ask for comments.
+
+### 8. Create a release branch
+
+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.
+
+```bash
+git checkout main
+git pull
+git checkout -b release-${RELEASE_VERSION}
+git push origin release-${RELEASE_VERSION}
+```
+
+Do **not** create or push the release/RC tag yet; that happens in [Build a release candidate](#build-a-release-candidate) after the source artifacts are staged.
+
+### 9. Bump version on main for the next development cycle
+
+So that `main` moves to the next version immediately after the release branch is cut, run the bump script and commit:
+
+```bash
+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.
+
+### 10. Optional: Create PRs for release blog and download page
+
+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**
+
+- [ ] Rust (and optionally just) installed and on PATH
+- [ ] No release-blocking issues (or triaged)
+- [ ] Environment variables set
+- [ ] Release branch created and pushed
+- [ ] Main branch bumped to `NEXT_VERSION` and pushed
+- [ ] Dependencies list generated and committed on main
+- [ ] (Optional) DISCUSS thread and/or tracking issue created
+- [ ] (Optional) PRs for blog and download page created but not merged
+
+## Build a release candidate
+
+Each 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](#fix-any-issues)).
+
+### 1. Set RC environment variables
+
+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.
+
+```bash
+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.
+
+### 2. Check out the release branch and create the tag
+
+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).
+
+```bash
+git checkout release-${RELEASE_VERSION}
+git pull
+git tag -s $RC_TAG -m "${RC_TAG}"
+git push origin $RC_TAG
+```
+
+Check CI: [Actions](https://github.com/apache/fluss-rust/actions) (Release Rust, Release Python).
+
+### 3. Create source release artifacts
+
+From the repository root (on the release branch, at the commit you tagged):
+
+```bash
+just release $RELEASE_VERSION
+# Or: ./scripts/release.sh $RELEASE_VERSION
+```
+
+This creates under `dist/`:
+
+- `fluss-rust-${RELEASE_VERSION}.tar.gz`
+- `fluss-rust-${RELEASE_VERSION}.tar.gz.sha512`
+- `fluss-rust-${RELEASE_VERSION}.tar.gz.asc`
+
+Verify with: `gpg --verify dist/fluss-rust-${RELEASE_VERSION}.tar.gz.asc dist/fluss-rust-${RELEASE_VERSION}.tar.gz`
+
+### 4. Stage artifacts to SVN (dist.apache.org dev)
+
+From the **fluss-rust** repo root, check out the Fluss dev area and add the release artifacts.
+
+```bash
+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}.* $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/](https://dist.apache.org/repos/dist/dev/incubator/fluss/)
+
+---
+
+**Checklist to proceed to the next step**
+
+- [ ] Source distribution built and signed under `dist/`
+- [ ] Artifacts staged to [dist.apache.org dev](https://dist.apache.org/repos/dist/dev/incubator/fluss/) under `$SVN_RC_DIR`
+- [ ] RC (or release) tag pushed to GitHub
+- [ ] CI for Release Rust / Release Python succeeded
+
+## Vote on the release candidate
+
+Share the release candidate for community review. If the project is in incubation, a [two-phase vote](https://incubator.apache.org/cookbook/#two_phase_vote_on_podling_releases) (Fluss community then Incubator PMC) may be required; otherwise one community vote is enough.
+
+### Fluss community vote
+
+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/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 the project docs (todo: add how to verify release).
Review Comment:
The vote email template still contains a TODO (“todo: add how to verify release”). Since this doc is meant to be executable, replace the TODO with concrete verification steps or a link to the relevant verification section/document.
```suggestion
Please download, verify, and test the release candidate by:
* downloading the source tarball from the RC location above;
* verifying the GPG signature and SHA512 checksum using the KEYS file;
* building from source and running the full test suite; and
* checking that version numbers, licenses, and basic functionality look correct.
```
##########
docs/generate-release-note.md:
##########
@@ -0,0 +1,28 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+# Generate Release Note
+
+Use GitHub's **Generate release notes** to produce a draft from merged PRs between tags. Categories (Added, Fixed, Docs, etc.) are configured in [.github/release.yml](../.github/release.yml).
+
+1. Go to [Create a new release](https://github.com/apache/fluss-rust/releases/new).
+2. In **Choose a tag**, pick the release tag (e.g. `v0.1.0`) or type `draft` if the tag does not exist yet.
Review Comment:
Using a placeholder tag name like `draft` is risky because GitHub may create/publish a real tag with that name. Prefer instructing users to pick the actual release/RC tag (or explicitly create a new tag in the UI with the intended version).
```suggestion
2. In **Choose a tag**, pick the release tag (e.g. `v0.1.0`). If the tag does not exist yet, create a new tag in the UI with the intended version (for example `v0.2.0` or `v0.2.0-rc1`) and select that tag.
```
##########
.github/workflows/release_python.yml:
##########
@@ -0,0 +1,137 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Publish the fluss Python binding to PyPI.
+# Trigger: push tag only (e.g. v0.1.0).
+# Pre-release tags (containing '-') publish to TestPyPI; release tags publish to PyPI.
+#
+# First publish: add repo secrets PYPI_API_TOKEN and/or TEST_PYPI_API_TOKEN (from pypi.org / test.pypi.org);
+# CI will use them. After Trusted Publishing is set up on PyPI/TestPyPI, you can remove the secrets.
+
+name: Release Python
+
+on:
+ push:
+ tags:
+ - "*"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+
+jobs:
+ sdist:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install protoc
+ run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
+
+ - uses: PyO3/maturin-action@v1
+ with:
+ working-directory: bindings/python
+ command: sdist
+ args: -o dist
+
+ - name: Upload sdist
+ uses: actions/upload-artifact@v4
+ with:
+ name: wheels-sdist
+ path: bindings/python/dist
+
+ wheels:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ include:
+ - { os: windows-latest }
+ - { os: macos-15-intel, target: "x86_64-apple-darwin" }
+ - { os: macos-15, target: "aarch64-apple-darwin" }
+ - { os: ubuntu-latest, target: "x86_64" }
+ - { os: ubuntu-latest, target: "aarch64", manylinux: "manylinux_2_28" }
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install protoc (Linux)
+ if: runner.os == 'Linux'
+ run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
+
+ - name: Install protoc (macOS)
+ if: runner.os == 'macOS'
+ run: brew install protobuf
+
+ - name: Install protoc (Windows)
+ if: runner.os == 'Windows'
+ run: choco install protobuf -y
+ shell: pwsh
+
+ - uses: PyO3/maturin-action@v1
+ with:
+ working-directory: bindings/python
+ target: ${{ matrix.target }}
+ command: build
+ args: --release -o dist -i python3.11
+ manylinux: ${{ matrix.manylinux || 'auto' }}
+ - uses: PyO3/maturin-action@v1
+ with:
+ working-directory: bindings/python
+ target: ${{ matrix.target }}
+ command: build
+ args: --release -o dist -i python3.12
+ manylinux: ${{ matrix.manylinux || 'auto' }}
+
+ - name: Upload wheels
+ uses: actions/upload-artifact@v4
+ with:
+ name: wheels-${{ matrix.os }}-${{ matrix.target || 'native' }}
+ path: bindings/python/dist
+
+ release:
+ name: Publish to PyPI
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ needs: [sdist, wheels]
+ if: startsWith(github.ref, 'refs/tags/')
+ steps:
+ - uses: actions/download-artifact@v4
+ with:
+ pattern: wheels-*
+ merge-multiple: true
+ path: bindings/python/dist
+
+ - name: Publish to TestPyPI (pre-release tags)
+ if: contains(github.ref, '-')
+ uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ repository-url: https://test.pypi.org/legacy/
+ skip-existing: true
+ packages-dir: bindings/python/dist
+ password: ${{ secrets.TEST_PYPI_API_TOKEN }}
+
+ - name: Publish to PyPI (release tags)
+ if: ${{ !contains(github.ref, '-') }}
+ uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ skip-existing: true
+ packages-dir: bindings/python/dist
+ password: ${{ secrets.PYPI_API_TOKEN }}
Review Comment:
Same issue as TestPyPI: this step always passes `password: ${{ secrets.PYPI_API_TOKEN }}`, so removing the secret will not fall back to Trusted Publishing via OIDC (it will attempt an empty token). Split into two steps or conditionally omit `password` when the secret is unset.
```suggestion
- name: Publish to PyPI (release tags via API token)
if: ${{ !contains(github.ref, '-') && secrets.PYPI_API_TOKEN != '' }}
uses: pypa/gh-action-pypi-publish@release/v1
with:
skip-existing: true
packages-dir: bindings/python/dist
password: ${{ secrets.PYPI_API_TOKEN }}
- name: Publish to PyPI (release tags via OIDC)
if: ${{ !contains(github.ref, '-') && secrets.PYPI_API_TOKEN == '' }}
uses: pypa/gh-action-pypi-publish@release/v1
with:
skip-existing: true
packages-dir: bindings/python/dist
```
##########
.github/workflows/release_rust.yml:
##########
@@ -0,0 +1,58 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Publish the fluss Rust crate to crates.io.
+# Trigger: push tag only (e.g. v0.1.0).
+# Pre-release tags (containing '-') do not publish; release tags publish to crates.io.
+#
+# First publish: add repo secret CARGO_REGISTRY_TOKEN (crates.io API token); CI will use it.
+# After crate exists: set up Trusted Publishing on the crate page, then remove the secret; CI will use OIDC.
+
+name: Release Rust
+
+on:
+ push:
+ tags:
+ - "*"
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install protoc
+ run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
+
+ - name: Dry run (crates/fluss)
+ run: cargo publish -p fluss-rs --dry-run
+
+ - name: Get crates.io token (OIDC)
+ if: secrets.CARGO_REGISTRY_TOKEN == ''
+ uses: rust-lang/crates-io-auth-action@v1
+ id: auth
+ with:
+ token-type: publish
+
+ - name: Publish fluss-rs to crates.io
+ if: startsWith(github.ref, 'refs/tags/') && !contains(github.ref, '-')
+ run: cargo publish -p fluss-rs --no-verify
Review Comment:
`cargo publish --no-verify` skips Cargo's publish verification checks and can allow publishing a crate that would otherwise be rejected (missing files, bad metadata, etc.). Prefer publishing without `--no-verify` and fix any verification failures explicitly.
```suggestion
run: cargo publish -p fluss-rs
```
##########
docs/creating-a-release.md:
##########
@@ -0,0 +1,452 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+# Creating a Fluss Rust Client Release
+
+This document describes in detail how to create a release of the **Fluss clients** (fluss-rust, fluss-python, fluss-cpp) from the [fluss-rust](https://github.com/apache/fluss-rust) repository. It is based on the [Creating a Fluss Release](https://fluss.apache.org/community/how-to-release/creating-a-fluss-release/) guide of the Apache Fluss project and the [release guide of Apache OpenDAL](https://nightlies.apache.org/opendal/opendal-docs-stable/community/release/); 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](https://www.apache.org/legal/release-policy.html) and [Release Distribution Policy](https://infra.apache.org/release-distribution.html).
+
+## Overview
+
+
+
+The release process consists of:
+
+1. [Decide to release](#decide-to-release)
+2. [Prepare for the release](#prepare-for-the-release)
+3. [Build a release candidate](#build-a-release-candidate)
+4. [Vote on the release candidate](#vote-on-the-release-candidate)
+5. [If necessary, fix any issues and go back to step 3](#fix-any-issues)
+6. [Finalize the release](#finalize-the-release)
+7. [Promote the release](#promote-the-release)
+
+## Decide to release
+
+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**
+
+- [ ] Community agrees to release
+- [ ] A Release Manager is selected
+
+## Prepare for the release
+
+### 0. One-time Release Manager setup
+
+Before your first release, perform one-time configuration. See **[Release Manager Preparation](https://fluss.apache.org/community/how-to-release/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)**
+
+- [ ] GPG key set up and published to [KEYS](https://downloads.apache.org/fluss/KEYS) or Apache account
+- [ ] Git configured to use your GPG key for signing tags
+
+### 1. Install Rust (and optional: just)
+
+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](https://rustup.rs/) (the version should match [rust-toolchain.toml](https://github.com/apache/fluss-rust/blob/main/rust-toolchain.toml) in the repo).
+
+```bash
+rustc --version
+cargo --version
+```
+
+To use `just release`, install [just](https://github.com/casey/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.
+
+### 2. Optional: Create a new Milestone in GitHub
+
+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.
+
+### 3. Optional: Triage release-blocking issues
+
+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.
+
+### 4. Clone fluss-rust into a fresh workspace
+
+Use a clean clone to avoid local changes affecting the release.
+
+```bash
+git clone https://github.com/apache/fluss-rust.git
+cd fluss-rust
+```
+
+### 5. Set up environment variables
+
+Set these once and use them in all following commands. (Bash syntax.)
+
+```bash
+export RELEASE_VERSION="0.1.0"
+export RELEASE_TAG="v${RELEASE_VERSION}"
+export SVN_RELEASE_DIR="fluss-rust-${RELEASE_VERSION}"
+# Only set if there is a previous release (for compare link in DISCUSS / release notes)
+export LAST_VERSION="0.0.9"
+export 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.
+
+### 6. Generate dependencies list
+
+[ASF release policy](https://www.apache.org/legal/release-policy.html) requires that every release comply with [ASF licensing policy](https://www.apache.org/legal/resolved.html) 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.
+
+1. Download and set up [cargo-deny](https://embarkstudios.github.io/cargo-deny/cli/index.html) (see cargo-deny docs).
+2. Run the script to update the dependency list, then commit on `main`:
+
+```bash
+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`.
+
+### 7. Optional: Start a [DISCUSS] thread
+
+On [Fluss Discussions](https://github.com/apache/fluss-rust/discussions) or the dev list:
+
+- **Subject:** `[DISCUSS] Release Apache Fluss clients (fluss-rust, fluss-python, fluss-cpp) $RELEASE_VERSION`
+- **Body:** Short rationale; if there is a previous release, add compare link: `https://github.com/apache/fluss-rust/compare/v${LAST_VERSION}...main`. Ask for comments.
+
+### 8. Create a release branch
+
+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.
+
+```bash
+git checkout main
+git pull
+git checkout -b release-${RELEASE_VERSION}
+git push origin release-${RELEASE_VERSION}
+```
+
+Do **not** create or push the release/RC tag yet; that happens in [Build a release candidate](#build-a-release-candidate) after the source artifacts are staged.
+
+### 9. Bump version on main for the next development cycle
+
+So that `main` moves to the next version immediately after the release branch is cut, run the bump script and commit:
+
+```bash
+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.
+
+### 10. Optional: Create PRs for release blog and download page
+
+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**
+
+- [ ] Rust (and optionally just) installed and on PATH
+- [ ] No release-blocking issues (or triaged)
+- [ ] Environment variables set
+- [ ] Release branch created and pushed
+- [ ] Main branch bumped to `NEXT_VERSION` and pushed
+- [ ] Dependencies list generated and committed on main
+- [ ] (Optional) DISCUSS thread and/or tracking issue created
+- [ ] (Optional) PRs for blog and download page created but not merged
+
+## Build a release candidate
+
+Each 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](#fix-any-issues)).
+
+### 1. Set RC environment variables
+
+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.
+
+```bash
+export RC_NUM="1"
+export RC_TAG="v${RELEASE_VERSION}-rc${RC_NUM}"
+export SVN_RC_DIR="fluss-rust-${RELEASE_VERSION}-rc${RC_NUM}"
Review Comment:
RC tag format here (`v${RELEASE_VERSION}-rc${RC_NUM}`) conflicts with the example in `scripts/release.sh` (`v0.1.0-rc.1`). Please standardize the RC tag naming scheme across the repo to avoid confusion and accidental tagging/publishing mistakes.
```suggestion
export RC_TAG="v${RELEASE_VERSION}-rc.${RC_NUM}"
export SVN_RC_DIR="fluss-rust-${RELEASE_VERSION}-rc.${RC_NUM}"
```
##########
docs/creating-a-release.md:
##########
@@ -0,0 +1,452 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+# Creating a Fluss Rust Client Release
+
+This document describes in detail how to create a release of the **Fluss clients** (fluss-rust, fluss-python, fluss-cpp) from the [fluss-rust](https://github.com/apache/fluss-rust) repository. It is based on the [Creating a Fluss Release](https://fluss.apache.org/community/how-to-release/creating-a-fluss-release/) guide of the Apache Fluss project and the [release guide of Apache OpenDAL](https://nightlies.apache.org/opendal/opendal-docs-stable/community/release/); 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](https://www.apache.org/legal/release-policy.html) and [Release Distribution Policy](https://infra.apache.org/release-distribution.html).
+
+## Overview
+
+
+
+The release process consists of:
+
+1. [Decide to release](#decide-to-release)
+2. [Prepare for the release](#prepare-for-the-release)
+3. [Build a release candidate](#build-a-release-candidate)
+4. [Vote on the release candidate](#vote-on-the-release-candidate)
+5. [If necessary, fix any issues and go back to step 3](#fix-any-issues)
+6. [Finalize the release](#finalize-the-release)
+7. [Promote the release](#promote-the-release)
+
+## Decide to release
+
+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**
+
+- [ ] Community agrees to release
+- [ ] A Release Manager is selected
+
+## Prepare for the release
+
+### 0. One-time Release Manager setup
+
+Before your first release, perform one-time configuration. See **[Release Manager Preparation](https://fluss.apache.org/community/how-to-release/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)**
+
+- [ ] GPG key set up and published to [KEYS](https://downloads.apache.org/fluss/KEYS) or Apache account
+- [ ] Git configured to use your GPG key for signing tags
+
+### 1. Install Rust (and optional: just)
+
+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](https://rustup.rs/) (the version should match [rust-toolchain.toml](https://github.com/apache/fluss-rust/blob/main/rust-toolchain.toml) in the repo).
+
+```bash
+rustc --version
+cargo --version
+```
+
+To use `just release`, install [just](https://github.com/casey/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.
+
+### 2. Optional: Create a new Milestone in GitHub
+
+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.
+
+### 3. Optional: Triage release-blocking issues
+
+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.
+
+### 4. Clone fluss-rust into a fresh workspace
+
+Use a clean clone to avoid local changes affecting the release.
+
+```bash
+git clone https://github.com/apache/fluss-rust.git
+cd fluss-rust
+```
+
+### 5. Set up environment variables
+
+Set these once and use them in all following commands. (Bash syntax.)
+
+```bash
+export RELEASE_VERSION="0.1.0"
+export RELEASE_TAG="v${RELEASE_VERSION}"
+export SVN_RELEASE_DIR="fluss-rust-${RELEASE_VERSION}"
+# Only set if there is a previous release (for compare link in DISCUSS / release notes)
+export LAST_VERSION="0.0.9"
+export 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.
+
+### 6. Generate dependencies list
+
+[ASF release policy](https://www.apache.org/legal/release-policy.html) requires that every release comply with [ASF licensing policy](https://www.apache.org/legal/resolved.html) 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.
+
+1. Download and set up [cargo-deny](https://embarkstudios.github.io/cargo-deny/cli/index.html) (see cargo-deny docs).
+2. Run the script to update the dependency list, then commit on `main`:
+
+```bash
+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`.
+
+### 7. Optional: Start a [DISCUSS] thread
+
+On [Fluss Discussions](https://github.com/apache/fluss-rust/discussions) or the dev list:
+
+- **Subject:** `[DISCUSS] Release Apache Fluss clients (fluss-rust, fluss-python, fluss-cpp) $RELEASE_VERSION`
+- **Body:** Short rationale; if there is a previous release, add compare link: `https://github.com/apache/fluss-rust/compare/v${LAST_VERSION}...main`. Ask for comments.
+
+### 8. Create a release branch
+
+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.
+
+```bash
+git checkout main
+git pull
+git checkout -b release-${RELEASE_VERSION}
+git push origin release-${RELEASE_VERSION}
+```
+
+Do **not** create or push the release/RC tag yet; that happens in [Build a release candidate](#build-a-release-candidate) after the source artifacts are staged.
+
+### 9. Bump version on main for the next development cycle
+
+So that `main` moves to the next version immediately after the release branch is cut, run the bump script and commit:
+
+```bash
+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.
+
+### 10. Optional: Create PRs for release blog and download page
+
+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**
+
+- [ ] Rust (and optionally just) installed and on PATH
+- [ ] No release-blocking issues (or triaged)
+- [ ] Environment variables set
+- [ ] Release branch created and pushed
+- [ ] Main branch bumped to `NEXT_VERSION` and pushed
+- [ ] Dependencies list generated and committed on main
+- [ ] (Optional) DISCUSS thread and/or tracking issue created
+- [ ] (Optional) PRs for blog and download page created but not merged
+
+## Build a release candidate
+
+Each 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](#fix-any-issues)).
+
+### 1. Set RC environment variables
+
+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.
+
+```bash
+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.
+
+### 2. Check out the release branch and create the tag
+
+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).
+
+```bash
+git checkout release-${RELEASE_VERSION}
+git pull
+git tag -s $RC_TAG -m "${RC_TAG}"
+git push origin $RC_TAG
+```
+
+Check CI: [Actions](https://github.com/apache/fluss-rust/actions) (Release Rust, Release Python).
+
+### 3. Create source release artifacts
+
+From the repository root (on the release branch, at the commit you tagged):
+
+```bash
+just release $RELEASE_VERSION
+# Or: ./scripts/release.sh $RELEASE_VERSION
+```
+
+This creates under `dist/`:
+
+- `fluss-rust-${RELEASE_VERSION}.tar.gz`
+- `fluss-rust-${RELEASE_VERSION}.tar.gz.sha512`
+- `fluss-rust-${RELEASE_VERSION}.tar.gz.asc`
+
+Verify with: `gpg --verify dist/fluss-rust-${RELEASE_VERSION}.tar.gz.asc dist/fluss-rust-${RELEASE_VERSION}.tar.gz`
+
+### 4. Stage artifacts to SVN (dist.apache.org dev)
+
+From the **fluss-rust** repo root, check out the Fluss dev area and add the release artifacts.
+
+```bash
+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}.* $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/](https://dist.apache.org/repos/dist/dev/incubator/fluss/)
+
+---
+
+**Checklist to proceed to the next step**
+
+- [ ] Source distribution built and signed under `dist/`
+- [ ] Artifacts staged to [dist.apache.org dev](https://dist.apache.org/repos/dist/dev/incubator/fluss/) under `$SVN_RC_DIR`
+- [ ] RC (or release) tag pushed to GitHub
+- [ ] CI for Release Rust / Release Python succeeded
+
+## Vote on the release candidate
+
+Share the release candidate for community review. If the project is in incubation, a [two-phase vote](https://incubator.apache.org/cookbook/#two_phase_vote_on_podling_releases) (Fluss community then Incubator PMC) may be required; otherwise one community vote is enough.
+
+### Fluss community vote
+
+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/fluss/KEYS
Review Comment:
This KEYS link points to the non-incubator path (`/fluss/KEYS`), but the RC artifacts are staged under `/incubator/fluss/`. For incubating projects, the KEYS link should typically match the incubator download area (or explain both paths, as you do later in the announce template).
```suggestion
* https://downloads.apache.org/incubator/fluss/KEYS
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@fluss.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] build: add how to release doc [fluss-rust]
Posted by "fresh-borzoni (via GitHub)" <gi...@apache.org>.
fresh-borzoni commented on code in PR #268:
URL: https://github.com/apache/fluss-rust/pull/268#discussion_r2777512589
##########
.github/workflows/release_python.yml:
##########
@@ -0,0 +1,152 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Publish the fluss Python binding to PyPI.
+# Trigger: push tag only (e.g. v0.1.0).
+# Pre-release tags (containing '-') publish to TestPyPI; release tags publish to PyPI.
+#
+# Token auth: set repo variable PYPI_USE_TOKEN_AUTH = 'true' and add secrets PYPI_API_TOKEN / TEST_PYPI_API_TOKEN.
+# Trusted Publishing (OIDC): leave PYPI_USE_TOKEN_AUTH unset; do not pass password so the action uses OIDC.
+
+name: Release Python
+
+on:
+ push:
+ tags:
+ - "*"
+
+concurrency:
Review Comment:
Do we wish to have the same for rust?
##########
.github/workflows/release_rust.yml:
##########
@@ -0,0 +1,58 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Publish the fluss Rust crate to crates.io.
+# Trigger: push tag only (e.g. v0.1.0).
+# Pre-release tags (containing '-') do not publish; release tags publish to crates.io.
+#
+# Token auth: set repo variable CARGO_USE_TOKEN_AUTH = 'true' and add secret CARGO_REGISTRY_TOKEN.
+# Trusted Publishing (OIDC): leave CARGO_USE_TOKEN_AUTH unset; token is obtained via OIDC (no secret).
+
+name: Release Rust
+
+on:
+ push:
+ tags:
+ - "*"
Review Comment:
nit: Do we wish to release on any tag pushes?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@fluss.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] build: add how to release doc [fluss-rust]
Posted by "luoyuxia (via GitHub)" <gi...@apache.org>.
luoyuxia commented on code in PR #268:
URL: https://github.com/apache/fluss-rust/pull/268#discussion_r2777574092
##########
.github/workflows/release_rust.yml:
##########
@@ -0,0 +1,58 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Publish the fluss Rust crate to crates.io.
+# Trigger: push tag only (e.g. v0.1.0).
+# Pre-release tags (containing '-') do not publish; release tags publish to crates.io.
+#
+# Token auth: set repo variable CARGO_USE_TOKEN_AUTH = 'true' and add secret CARGO_REGISTRY_TOKEN.
+# Trusted Publishing (OIDC): leave CARGO_USE_TOKEN_AUTH unset; token is obtained via OIDC (no secret).
+
+name: Release Rust
+
+on:
+ push:
+ tags:
+ - "*"
Review Comment:
let's limit to `v*`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@fluss.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [PR] build: add how to release doc [fluss-rust]
Posted by "Copilot (via GitHub)" <gi...@apache.org>.
Copilot commented on code in PR #268:
URL: https://github.com/apache/fluss-rust/pull/268#discussion_r2777598384
##########
.github/workflows/release_python.yml:
##########
@@ -0,0 +1,174 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Publish the fluss Python binding to PyPI.
+# Trigger: push tag only (e.g. v0.1.0).
+# Pre-release tags (containing '-') publish to TestPyPI; release tags publish to PyPI.
+#
+# Token auth: set repo variable PYPI_USE_TOKEN_AUTH = 'true' and add secrets PYPI_API_TOKEN / TEST_PYPI_API_TOKEN.
+# Trusted Publishing (OIDC): leave PYPI_USE_TOKEN_AUTH unset; do not pass password so the action uses OIDC.
+
+name: Release Python
+
+on:
+ push:
+ tags:
+ - "v*" # Only version-like tags (e.g. v0.1.0, v0.1.0-rc1); avoids running on arbitrary tags
+
Review Comment:
The tag trigger pattern `v*` matches any tag starting with `v` (including non-version tags), so this workflow may run unexpectedly. Consider narrowing it (e.g., `v[0-9]*`) so only version-like tags trigger the release jobs.
##########
scripts/bump-version.sh:
##########
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to you under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Bump version in root Cargo.toml ([workspace.package] and [workspace.dependencies] fluss-rs).
+# Run from repo root. Use after cutting a release branch so main is set to the next version.
+#
+# Usage: ./scripts/bump-version.sh <current_version> <next_version>
+# e.g. ./scripts/bump-version.sh 0.1.0 0.1.1
+# Or with env vars: ./scripts/bump-version.sh $RELEASE_VERSION $NEXT_VERSION
+
+set -e
+
+if [ -z "$1" ] || [ -z "$2" ]; then
+ echo "Usage: $0 <current_version> <next_version>"
+ echo " e.g. $0 0.1.0 0.1.1"
+ exit 1
+fi
+
+FROM_VERSION="$1"
+TO_VERSION="$2"
+
+REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+cd "$REPO_ROOT"
+
+if [ ! -f Cargo.toml ]; then
+ echo "Cargo.toml not found. Run from repo root."
+ exit 1
+fi
+
+# Replace version = "X.Y.Z" with version = "TO_VERSION" (all occurrences in root Cargo.toml)
+case "$(uname -s)" in
+ Darwin)
+ sed -i '' "s/version = \"${FROM_VERSION}\"/version = \"${TO_VERSION}\"/g" Cargo.toml
+ ;;
+ *)
+ sed -i "s/version = \"${FROM_VERSION}\"/version = \"${TO_VERSION}\"/g" Cargo.toml
Review Comment:
This `sed` replacement updates every `version = "<from>"` occurrence in the root Cargo.toml. That works today, but it can accidentally bump unrelated dependency versions if they happen to match `FROM_VERSION`. Consider restricting the edit to the `[workspace.package]` section and the `workspace.dependencies.fluss` entry (or using a small TOML-aware update) to avoid unintended replacements.
```suggestion
# Replace version only in [workspace.package] and workspace.dependencies.fluss in root Cargo.toml
case "$(uname -s)" in
Darwin)
sed -i '' \
-e "/\[workspace.package\]/,/^\[/{s/^version = \"${FROM_VERSION}\"/version = \"${TO_VERSION}\"/}" \
-e "/\[workspace.dependencies\]/,/^\[/{/fluss/{s/version = \"${FROM_VERSION}\"/version = \"${TO_VERSION}\"/}}" \
Cargo.toml
;;
*)
sed -i \
-e "/\[workspace.package\]/,/^\[/{s/^version = \"${FROM_VERSION}\"/version = \"${TO_VERSION}\"/}" \
-e "/\[workspace.dependencies\]/,/^\[/{/fluss/{s/version = \"${FROM_VERSION}\"/version = \"${TO_VERSION}\"/}}" \
Cargo.toml
```
##########
docs/creating-a-release.md:
##########
@@ -0,0 +1,453 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+# Creating a Fluss Rust Client Release
+
+This document describes in detail how to create a release of the **Fluss clients** (fluss-rust, fluss-python, fluss-cpp) from the [fluss-rust](https://github.com/apache/fluss-rust) repository. It is based on the [Creating a Fluss Release](https://fluss.apache.org/community/how-to-release/creating-a-fluss-release/) guide of the Apache Fluss project and the [release guide of Apache OpenDAL](https://nightlies.apache.org/opendal/opendal-docs-stable/community/release/); 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](https://www.apache.org/legal/release-policy.html) and [Release Distribution Policy](https://infra.apache.org/release-distribution.html).
+
+## Overview
+
+
+
+The release process consists of:
+
+1. [Decide to release](#decide-to-release)
+2. [Prepare for the release](#prepare-for-the-release)
+3. [Build a release candidate](#build-a-release-candidate)
+4. [Vote on the release candidate](#vote-on-the-release-candidate)
+5. [If necessary, fix any issues and go back to step 3](#fix-any-issues)
+6. [Finalize the release](#finalize-the-release)
+7. [Promote the release](#promote-the-release)
+
+## Decide to release
+
+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**
+
+- [ ] Community agrees to release
+- [ ] A Release Manager is selected
+
+## Prepare for the release
+
+### 0. One-time Release Manager setup
+
+Before your first release, perform one-time configuration. See **[Release Manager Preparation](https://fluss.apache.org/community/how-to-release/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)**
+
+- [ ] GPG key set up and published to [KEYS](https://downloads.apache.org/incubator/fluss/KEYS) or Apache account
+- [ ] Git configured to use your GPG key for signing tags
+
+### 1. Install Rust (and optional: just)
+
+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](https://rustup.rs/) (the version should match [rust-toolchain.toml](https://github.com/apache/fluss-rust/blob/main/rust-toolchain.toml) in the repo). The dependency list script (`scripts/dependencies.py`) requires **Python 3.11+**.
+
+```bash
+rustc --version
+cargo --version
+```
+
+To use `just release`, install [just](https://github.com/casey/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.
+
+### 2. Optional: Create a new Milestone in GitHub
+
+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.
+
+### 3. Optional: Triage release-blocking issues
+
+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.
+
+### 4. Clone fluss-rust into a fresh workspace
+
+Use a clean clone to avoid local changes affecting the release.
+
+```bash
+git clone https://github.com/apache/fluss-rust.git
+cd fluss-rust
+```
+
+### 5. Set up environment variables
+
+Set these once and use them in all following commands. (Bash syntax.)
+
+```bash
+export RELEASE_VERSION="0.1.0"
+export RELEASE_TAG="v${RELEASE_VERSION}"
+export SVN_RELEASE_DIR="fluss-rust-${RELEASE_VERSION}"
+# Only set if there is a previous release (for compare link in DISCUSS / release notes)
+export LAST_VERSION="0.0.9"
+export 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.
+
+### 6. Generate dependencies list
+
+[ASF release policy](https://www.apache.org/legal/release-policy.html) requires that every release comply with [ASF licensing policy](https://www.apache.org/legal/resolved.html) 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.
+
+1. Download and set up [cargo-deny](https://embarkstudios.github.io/cargo-deny/cli/index.html) (see cargo-deny docs).
+2. Run the script to update the dependency list (requires **Python 3.11+** for the release tooling), then commit on `main`:
+
+```bash
+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`.
+
+### 7. Optional: Start a [DISCUSS] thread
+
+On [Fluss Discussions](https://github.com/apache/fluss-rust/discussions) or the dev list:
+
+- **Subject:** `[DISCUSS] Release Apache Fluss clients (fluss-rust, fluss-python, fluss-cpp) $RELEASE_VERSION`
+- **Body:** Short rationale; if there is a previous release, add compare link: `https://github.com/apache/fluss-rust/compare/v${LAST_VERSION}...main`. Ask for comments.
+
+### 8. Create a release branch
+
+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.
+
+```bash
+git checkout main
+git pull
+git checkout -b release-${RELEASE_VERSION}
+git push origin release-${RELEASE_VERSION}
+```
+
+Do **not** create or push the release/RC tag yet; that happens in [Build a release candidate](#build-a-release-candidate) after the source artifacts are staged.
+
+### 9. Bump version on main for the next development cycle
+
+So that `main` moves to the next version immediately after the release branch is cut, run the bump script and commit:
+
+```bash
+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.
+
+### 10. Optional: Create PRs for release blog and download page
+
+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**
+
+- [ ] Rust (and optionally just) installed and on PATH
+- [ ] Python 3.11+ for dependency list script
+- [ ] No release-blocking issues (or triaged)
+- [ ] Environment variables set
+- [ ] Release branch created and pushed
+- [ ] Main branch bumped to `NEXT_VERSION` and pushed
+- [ ] Dependencies list generated and committed on main
+- [ ] (Optional) DISCUSS thread and/or tracking issue created
+- [ ] (Optional) PRs for blog and download page created but not merged
+
+## Build a release candidate
+
+Each 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](#fix-any-issues)).
+
+### 1. Set RC environment variables
+
+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.
+
+```bash
+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.
+
+### 2. Check out the release branch and create the tag
+
+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).
+
+```bash
+git checkout release-${RELEASE_VERSION}
+git pull
+git tag -s $RC_TAG -m "${RC_TAG}"
+git push origin $RC_TAG
+```
+
+Check CI: [Actions](https://github.com/apache/fluss-rust/actions) (Release Rust, Release Python).
+
+### 3. Create source release artifacts
+
+From the repository root (on the release branch, at the commit you tagged):
+
+```bash
+just release $RELEASE_VERSION
+# Or: ./scripts/release.sh $RELEASE_VERSION
+```
+
+This creates under `dist/`:
+
+- `fluss-rust-${RELEASE_VERSION}.tar.gz`
+- `fluss-rust-${RELEASE_VERSION}.tar.gz.sha512`
+- `fluss-rust-${RELEASE_VERSION}.tar.gz.asc`
+
+Verify with: `gpg --verify dist/fluss-rust-${RELEASE_VERSION}.tar.gz.asc dist/fluss-rust-${RELEASE_VERSION}.tar.gz`
+
+### 4. Stage artifacts to SVN (dist.apache.org dev)
+
+From the **fluss-rust** repo root, check out the Fluss dev area and add the release artifacts.
+
+```bash
+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}.* $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/](https://dist.apache.org/repos/dist/dev/incubator/fluss/)
+
+---
+
+**Checklist to proceed to the next step**
+
+- [ ] Source distribution built and signed under `dist/`
+- [ ] Artifacts staged to [dist.apache.org dev](https://dist.apache.org/repos/dist/dev/incubator/fluss/) under `$SVN_RC_DIR`
+- [ ] RC (or release) tag pushed to GitHub
+- [ ] CI for Release Rust / Release Python succeeded
+
+## Vote on the release candidate
+
+Share the release candidate for community review. If the project is in incubation, a [two-phase vote](https://incubator.apache.org/cookbook/#two_phase_vote_on_podling_releases) (Fluss community then Incubator PMC) may be required; otherwise one community vote is enough.
+
+### Fluss community vote
+
+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 the project docs (todo: add how to verify release).
+
Review Comment:
The vote email template still contains a TODO for verification steps. This is likely to be copy/pasted into a real vote thread, so it should link to concrete verification instructions (even if minimal: sha512 + GPG verify commands) rather than a placeholder.
```suggestion
Please download, verify, and test the release candidate. For example, on a Unix-like system:
* Verify the SHA-512 checksum of the source tarball:
sha512sum apache-fluss-*.tar.gz
* Verify the GPG signature using the KEYS file:
gpg --import KEYS
gpg --verify apache-fluss-*.tar.gz.asc apache-fluss-*.tar.gz
```
##########
.github/workflows/release_rust.yml:
##########
@@ -0,0 +1,60 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Publish the fluss Rust crate to crates.io.
+# Trigger: push tag only (e.g. v0.1.0).
+# Pre-release tags (containing '-') do not publish; release tags publish to crates.io.
+#
+# Token auth: set repo variable CARGO_USE_TOKEN_AUTH = 'true' and add secret CARGO_REGISTRY_TOKEN.
+# Trusted Publishing (OIDC): leave CARGO_USE_TOKEN_AUTH unset; token is obtained via OIDC (no secret).
+
+name: Release Rust
+
+on:
+ push:
+ tags:
+ - "v*" # Only version-like tags (e.g. v0.1.0, v0.1.0-rc1); avoids running on arbitrary tags
+
Review Comment:
The tag trigger pattern `v*` will also match non-version tags like `vfoo`, which contradicts the comment and can cause the release workflow to run unexpectedly. Consider tightening the pattern (e.g., `v[0-9]*`) and relying on `verify-tag-version` as a secondary guard.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: issues-unsubscribe@fluss.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org