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
+
+![Release process overview](assets/release-guide.png)
+
+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
+
+![Release process overview](assets/release-guide.png)
+
+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
+
+![Release process overview](assets/release-guide.png)
+
+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: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to