This is an automated email from the ASF dual-hosted git repository.

lzljs3620320 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/paimon-rust.git


The following commit(s) were added to refs/heads/main by this push:
     new a3d535d  release: add document for how to release and how to verify a 
release (#235)
a3d535d is described below

commit a3d535d3bfd7bd40a5e515b7ce4b1488fb08132f
Author: yuxia Luo <[email protected]>
AuthorDate: Sat Apr 11 11:54:11 2026 +0800

    release: add document for how to release and how to verify a release (#235)
---
 .licenserc.yaml                                   |   1 +
 Cargo.toml                                        |   2 +-
 NOTICE                                            |   2 +-
 bindings/go/DEPENDENCIES.md                       |  23 ++
 bindings/go/DEPENDENCIES.rust.tsv                 |   0
 bindings/python/NOTICE                            |   2 +-
 deny.toml                                         |   5 +-
 docs/mkdocs.yml                                   |   5 +-
 docs/src/datafusion.md                            |   4 +-
 docs/src/getting-started.md                       |   4 +-
 docs/src/release/creating-a-release.md            | 393 ++++++++++++++++++++++
 docs/src/release/img/release-guide.png            | Bin 0 -> 363329 bytes
 docs/src/release/verifying-a-release-candidate.md | 142 ++++++++
 scripts/bump-version.sh                           |  54 +++
 scripts/constants.py                              |  45 +++
 scripts/dependencies.py                           |  96 ++++++
 scripts/release.sh                                |  67 ++++
 17 files changed, 836 insertions(+), 9 deletions(-)

diff --git a/.licenserc.yaml b/.licenserc.yaml
index c9e9755..a8d27a4 100644
--- a/.licenserc.yaml
+++ b/.licenserc.yaml
@@ -26,6 +26,7 @@ header:
     - ".github/PULL_REQUEST_TEMPLATE.md"
     - "crates/paimon/tests/**/*.json"
     - "**/go.sum"
+    - "**/DEPENDENCIES.*.tsv"
     - ".devcontainer/devcontainer.json"
     - "bindings/python/python/pypaimon_rust/py.typed"
 
diff --git a/Cargo.toml b/Cargo.toml
index c2dd6a5..5677a5e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,7 +20,7 @@ resolver = "2"
 members = ["crates/paimon", "crates/integration_tests", "bindings/c", 
"bindings/python", "crates/integrations/datafusion"]
 
 [workspace.package]
-version = "0.0.0"
+version = "0.1.0"
 edition = "2021"
 homepage = "https://paimon.apache.org/docs/rust/";
 repository = "https://github.com/apache/paimon-rust";
diff --git a/NOTICE b/NOTICE
index f5a777b..79131e5 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,5 +1,5 @@
 Apache Paimon Rust
-Copyright 2024 The Apache Software Foundation
+Copyright 2024-2026 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
diff --git a/bindings/go/DEPENDENCIES.md b/bindings/go/DEPENDENCIES.md
new file mode 100644
index 0000000..832e4e0
--- /dev/null
+++ b/bindings/go/DEPENDENCIES.md
@@ -0,0 +1,23 @@
+<!--
+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.
+-->
+
+# Dependencies
+
+Paimon Go Binding is based on the C Binding.
+Installation of libffi is required.
diff --git a/bindings/go/DEPENDENCIES.rust.tsv 
b/bindings/go/DEPENDENCIES.rust.tsv
new file mode 100644
index 0000000..e69de29
diff --git a/bindings/python/NOTICE b/bindings/python/NOTICE
index f5a777b..79131e5 100644
--- a/bindings/python/NOTICE
+++ b/bindings/python/NOTICE
@@ -1,5 +1,5 @@
 Apache Paimon Rust
-Copyright 2024 The Apache Software Foundation
+Copyright 2024-2026 The Apache Software Foundation
 
 This product includes software developed at
 The Apache Software Foundation (http://www.apache.org/).
diff --git a/deny.toml b/deny.toml
index 02dd81b..98a4dec 100644
--- a/deny.toml
+++ b/deny.toml
@@ -34,4 +34,7 @@ exceptions = [
     { crate = "webpki-roots", allow = [
         "CDLA-Permissive-2.0",
     ] },
-]
\ No newline at end of file
+    # The MPL license is allowed (binary-only):
+    # https://www.apache.org/legal/resolved.html#category-b
+    { allow = ["MPL-2.0"], crate = "generational-arena" },
+]
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index 2b756e2..a23c05a 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -51,8 +51,11 @@ nav:
   - DataFusion Integration: datafusion.md
   - Go Integration: go-binding.md
   - Architecture: architecture.md
-  - Releases: releases.md
   - Contributing: contributing.md
+  - Releases:
+    - Releases: releases.md
+    - Creating a Release: release/creating-a-release.md
+    - Verifying a Release Candidate: release/verifying-a-release-candidate.md
 
 markdown_extensions:
   - admonition
diff --git a/docs/src/datafusion.md b/docs/src/datafusion.md
index 824d1f3..18930b6 100644
--- a/docs/src/datafusion.md
+++ b/docs/src/datafusion.md
@@ -25,8 +25,8 @@ under the License.
 
 ```toml
 [dependencies]
-paimon = "0.0.0"
-paimon-datafusion = "0.0.0"
+paimon = "0.1.0"
+paimon-datafusion = "0.1.0"
 datafusion = "52"
 tokio = { version = "1", features = ["full"] }
 ```
diff --git a/docs/src/getting-started.md b/docs/src/getting-started.md
index 7763fea..3e5df5d 100644
--- a/docs/src/getting-started.md
+++ b/docs/src/getting-started.md
@@ -25,7 +25,7 @@ Add `paimon` to your `Cargo.toml`:
 
 ```toml
 [dependencies]
-paimon = "0.0.0"
+paimon = "0.1.0"
 tokio = { version = "1", features = ["full"] }
 ```
 
@@ -33,7 +33,7 @@ By default, the `storage-fs` (local filesystem) and 
`storage-memory` (in-memory)
 
 ```toml
 [dependencies]
-paimon = { version = "0.0.0", features = ["storage-s3"] }
+paimon = { version = "0.1.0", features = ["storage-s3"] }
 ```
 
 Available storage features:
diff --git a/docs/src/release/creating-a-release.md 
b/docs/src/release/creating-a-release.md
new file mode 100644
index 0000000..6b2ae7c
--- /dev/null
+++ b/docs/src/release/creating-a-release.md
@@ -0,0 +1,393 @@
+<!--
+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 Release
+
+This guide describes how to create a release of Apache Paimon Rust, including 
the Rust crates, Python binding, and Go binding. It follows the [ASF 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](img/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)
+
+### Automated Publishing
+
+When a version tag is pushed, GitHub Actions automatically publishes the 
language-specific artifacts:
+
+| Component        | Tag Pattern              | Published To      | 
Pre-release (`-rc`) Behavior          |
+|------------------|--------------------------|-------------------|---------------------------------------|
+| Rust crates      | `v0.1.0`                 | crates.io         | Dry-run 
only                          |
+| Python binding   | `v0.1.0`                 | PyPI              | Publishes 
to TestPyPI                 |
+| Go binding       | `v0.1.0`                 | Go module proxy   | Publishes 
as `bindings/go/vX.Y.Z-rcN` |
+
+The Release Manager's primary responsibility is managing the **source 
release** (tarball + signature) and coordinating the community vote. Language 
artifact publishing is handled by CI once the final tag is pushed.
+
+## 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 on the dev [mailing 
list](https://lists.apache.org/[email protected]), giving a 
short rationale and nominating a committer as Release Manager (including 
themselves).
+
+**Checklist**
+
+- [ ] Community agrees to release
+- [ ] A Release Manager is selected
+
+## Prepare for the Release
+
+### One-time Release Manager Setup
+
+Before your first release, complete the following setup.
+
+#### GPG Key
+
+1. Install GnuPG if not already available:
+
+    ```bash
+    # macOS
+    brew install gnupg
+
+    # Ubuntu / Debian
+    sudo apt install gnupg2
+    ```
+
+2. Generate a key pair:
+
+    ```bash
+    gpg --full-gen-key
+    ```
+
+    When prompted, select:
+
+    - Key type: **RSA and RSA** (option 1)
+    - Key size: **4096**
+    - Validity: **0** (does not expire)
+    - Real name and email: use your **Apache name and `@apache.org` email**
+
+3. List your keys to find your key ID (the 8-digit hex string in the `pub` 
line):
+
+    ```bash
+    gpg --list-keys --keyid-format short
+    ```
+
+    Example output:
+
+    ```text
+    pub   rsa4096/845E6689 2024-01-01 [SC]
+          ABCDEF1234567890ABCDEF1234567890845E6689
+    uid         [ultimate] Your Name <[email protected]>
+    sub   rsa4096/12345678 2024-01-01 [E]
+    ```
+
+    In this example, the key ID is `845E6689`. Replace `<YOUR_KEY_ID>` with 
your actual key ID in the following steps.
+
+4. Upload your public key to the Ubuntu key server:
+
+    ```bash
+    gpg --keyserver hkps://keyserver.ubuntu.com --send-keys <YOUR_KEY_ID>
+    ```
+
+5. Append your key to the project 
[KEYS](https://downloads.apache.org/paimon/KEYS) file (requires PMC write 
access):
+
+    ```bash
+    svn co https://dist.apache.org/repos/dist/release/paimon/ 
paimon-dist-release --depth=files
+    cd paimon-dist-release
+    (gpg --list-sigs <YOUR_KEY_ID> && gpg --armor --export <YOUR_KEY_ID>) >> 
KEYS
+    svn ci -m "Add <YOUR_NAME>'s public key"
+    ```
+
+    !!! note
+        Never remove existing keys from the KEYS file — users may need them to 
verify older releases.
+
+6. Configure Git to sign tags with your key:
+
+    ```bash
+    git config --global user.signingkey <YOUR_KEY_ID>
+    ```
+
+    Omit `--global` to only configure signing for the current repository.
+
+7. (Optional) Upload the GPG public key to your GitHub account:
+
+    Go to [https://github.com/settings/keys](https://github.com/settings/keys) 
and add your GPG key. Make sure the email associated with the key is also added 
at [https://github.com/settings/emails](https://github.com/settings/emails), 
otherwise signed commits and tags may show as "Unverified".
+
+#### GitHub Actions Secrets
+
+Ensure the following repository secrets are configured:
+
+- `CARGO_REGISTRY_TOKEN` — for crates.io publishing
+- `PYPI_API_TOKEN` — for PyPI publishing
+- `TEST_PYPI_API_TOKEN` — for TestPyPI publishing
+
+### Clone into a fresh workspace
+
+Use a clean clone to avoid local changes affecting the release.
+
+```bash
+git clone https://github.com/apache/paimon-rust.git
+cd paimon-rust
+```
+
+### Set up environment variables
+
+```bash
+RELEASE_VERSION="0.1.0"
+SHORT_RELEASE_VERSION="0.1"
+NEXT_VERSION="0.2.0"
+RELEASE_TAG="v${RELEASE_VERSION}"
+RC_NUM="1"
+RC_TAG="v${RELEASE_VERSION}-rc${RC_NUM}"
+```
+
+### 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). Generate and commit a 
dependency list on `main` **before** creating the release branch, so both 
`main` and the release branch have the same list.
+
+1. Install [cargo-deny](https://embarkstudios.github.io/cargo-deny/):
+
+    ```bash
+    cargo install cargo-deny
+    ```
+
+2. Generate the dependency list (requires **Python 3.11+**):
+
+    ```bash
+    git checkout main
+    git pull
+    python3 scripts/dependencies.py generate
+    ```
+
+    This creates a `DEPENDENCIES.rust.tsv` file for the workspace root and 
each member crate.
+
+3. Commit the result:
+
+    ```bash
+    git add **/DEPENDENCIES*.tsv
+    git commit -m "chore: update dependency list for release 
${RELEASE_VERSION}"
+    git push origin main
+    ```
+
+To only check licenses without generating files: `python3 
scripts/dependencies.py check`.
+
+### Create a release branch
+
+From `main`, create a release branch:
+
+```bash
+git checkout -b release-${SHORT_RELEASE_VERSION}
+git push origin release-${SHORT_RELEASE_VERSION}
+```
+
+### Bump version on main for next development cycle
+
+After cutting the release branch, bump `main` to the next version so that 
ongoing development does not use the released version number:
+
+```bash
+git checkout main
+./scripts/bump-version.sh ${RELEASE_VERSION} ${NEXT_VERSION}
+git add Cargo.toml
+git commit -m "chore: bump version to ${NEXT_VERSION}"
+git push origin main
+```
+
+The script updates `version` in root `Cargo.toml` (`[workspace.package]` and 
`[workspace.dependencies]`). All member crates inherit the workspace version.
+
+### Optional: Create PRs for release blog and download page
+
+If the project website has a release blog or download page, create pull 
requests to add the new version. **Do not merge these PRs until the release is 
finalized.**
+
+## Build a Release Candidate
+
+### Create the RC tag
+
+Check out the release branch, create a signed RC tag, and push it. Pushing the 
tag triggers CI workflows for all three components.
+
+```bash
+git checkout release-${SHORT_RELEASE_VERSION}
+git pull
+git tag -s ${RC_TAG} -m "${RC_TAG}"
+git push origin ${RC_TAG}
+```
+
+After pushing, verify in [GitHub 
Actions](https://github.com/apache/paimon-rust/actions) that all release 
workflows succeed:
+
+- **Release Rust** — dry-run check for RC tags
+- **Release Python Binding** — publishes to TestPyPI
+- **Release Go Binding** — builds embedded libraries for all platforms, 
creates `bindings/go/${RC_TAG}` tag
+
+### Create source release artifacts
+
+From the repository root (on the release branch, at the commit you tagged):
+
+```bash
+./scripts/release.sh ${RELEASE_VERSION}
+```
+
+This creates the following under `dist/`:
+
+- `paimon-rust-${RELEASE_VERSION}.tar.gz` — source archive
+- `paimon-rust-${RELEASE_VERSION}.tar.gz.asc` — GPG signature
+- `paimon-rust-${RELEASE_VERSION}.tar.gz.sha512` — SHA-512 checksum
+
+The script automatically generates the archive from `HEAD` via `git archive`, 
signs it with your GPG key, and verifies the signature.
+
+### Stage artifacts to SVN
+
+Upload the source release to the ASF dev area:
+
+```bash
+svn checkout https://dist.apache.org/repos/dist/dev/paimon/ paimon-dist-dev 
--depth=immediates
+cd paimon-dist-dev
+mkdir paimon-rust-${RELEASE_VERSION}-rc${RC_NUM}
+cp ../paimon-rust-${RELEASE_VERSION}.tar.gz* 
paimon-rust-${RELEASE_VERSION}-rc${RC_NUM}/
+svn add paimon-rust-${RELEASE_VERSION}-rc${RC_NUM}
+svn commit -m "Add paimon-rust ${RELEASE_VERSION} RC${RC_NUM}"
+```
+
+**Checklist**
+
+- [ ] RC tag pushed and CI workflows succeeded
+- [ ] Source tarball, signature, and checksum staged to [dist.apache.org 
dev](https://dist.apache.org/repos/dist/dev/paimon/)
+
+## Vote on the Release Candidate
+
+Start a vote on the dev mailing list.
+
+**Subject:** `[VOTE] Release Apache Paimon Rust ${RELEASE_VERSION} 
(RC${RC_NUM})`
+
+**Body:**
+
+```text
+Hi everyone,
+
+Please review and vote on release candidate #${RC_NUM} for Apache Paimon Rust 
${RELEASE_VERSION}.
+
+[ ] +1 Approve the release
+[ ] +0 No opinion
+[ ] -1 Do not approve (please provide specific comments)
+
+The release candidate is available at:
+https://dist.apache.org/repos/dist/dev/paimon/paimon-rust-${RELEASE_VERSION}-rc${RC_NUM}/
+
+Git tag:
+https://github.com/apache/paimon-rust/releases/tag/${RC_TAG}
+
+KEYS for signature verification:
+https://downloads.apache.org/paimon/KEYS
+
+The vote will be open for at least 72 hours.
+
+Thanks,
+Release Manager
+```
+
+After the vote passes, send a result email:
+
+**Subject:** `[RESULT][VOTE] Release Apache Paimon Rust ${RELEASE_VERSION} 
(RC${RC_NUM})`
+
+## Fix Any Issues
+
+If the vote reveals issues:
+
+1. Fix them on the release branch via normal PRs.
+2. Remove the old RC from dist dev (optional):
+
+```bash
+cd paimon-dist-dev
+svn remove paimon-rust-${RELEASE_VERSION}-rc${RC_NUM}
+svn commit -m "Remove paimon-rust ${RELEASE_VERSION} RC${RC_NUM} (superseded)"
+```
+
+3. Increment `RC_NUM`, then go back to [Build a release 
candidate](#build-a-release-candidate).
+
+## Finalize the Release
+
+### Push the release tag
+
+Once the vote passes, create and push the final release tag. This triggers CI 
to publish to crates.io, PyPI, and Go module proxy automatically.
+
+```bash
+git checkout ${RC_TAG}
+git tag -s ${RELEASE_TAG} -m "Release Apache Paimon Rust ${RELEASE_VERSION}"
+git push origin ${RELEASE_TAG}
+```
+
+### Move source artifacts to the release repository
+
+```bash
+svn mv -m "Release paimon-rust ${RELEASE_VERSION}" \
+  
https://dist.apache.org/repos/dist/dev/paimon/paimon-rust-${RELEASE_VERSION}-rc${RC_NUM}
 \
+  
https://dist.apache.org/repos/dist/release/paimon/paimon-rust-${RELEASE_VERSION}
+```
+
+### Verify published artifacts
+
+- **Rust:** [crates.io/crates/paimon](https://crates.io/crates/paimon) shows 
version `${RELEASE_VERSION}`
+- **Python:** [PyPI — pypaimon](https://pypi.org/project/pypaimon/) shows 
version `${RELEASE_VERSION}`
+- **Go:** `go list -m 
github.com/apache/paimon-rust/bindings/go@v${RELEASE_VERSION}` resolves
+
+### Create GitHub Release
+
+1. Go to [Releases — New 
release](https://github.com/apache/paimon-rust/releases/new).
+2. Choose tag `${RELEASE_TAG}`.
+3. Click **Generate release notes** and review.
+4. Click **Publish release**.
+
+**Checklist**
+
+- [ ] Release tag pushed; CI published to crates.io, PyPI, and Go module proxy
+- [ ] Source artifacts moved to [dist 
release](https://dist.apache.org/repos/dist/release/paimon/)
+- [ ] GitHub Release created
+
+## Promote the Release
+
+### Update the Releases page
+
+Update the [Releases](../releases.md) page: move the released version from 
"Upcoming" to "Past Releases" with a summary of key features and a link to the 
GitHub release notes.
+
+### Announce the release
+
+Wait at least 24 hours after finalizing. Send the announcement to 
`[email protected]` and `[email protected]` using your `@apache.org` 
email in **plain text**.
+
+**Subject:** `[ANNOUNCE] Release Apache Paimon Rust ${RELEASE_VERSION}`
+
+**Body:**
+
+```text
+The Apache Paimon community is pleased to announce the release of
+Apache Paimon Rust ${RELEASE_VERSION}.
+
+Rust:   cargo add paimon
+Python: pip install pypaimon
+Go:     go get github.com/apache/paimon-rust/bindings/go@v${RELEASE_VERSION}
+
+Release notes:
+https://github.com/apache/paimon-rust/releases/tag/v${RELEASE_VERSION}
+
+Thanks to all contributors!
+```
diff --git a/docs/src/release/img/release-guide.png 
b/docs/src/release/img/release-guide.png
new file mode 100644
index 0000000..bf7602d
Binary files /dev/null and b/docs/src/release/img/release-guide.png differ
diff --git a/docs/src/release/verifying-a-release-candidate.md 
b/docs/src/release/verifying-a-release-candidate.md
new file mode 100644
index 0000000..424e850
--- /dev/null
+++ b/docs/src/release/verifying-a-release-candidate.md
@@ -0,0 +1,142 @@
+<!--
+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.
+-->
+
+# Verifying a Release Candidate
+
+This document describes how to verify a release candidate (RC) of **Apache 
Paimon Rust** (Rust crates, Python binding, Go binding) from the 
[paimon-rust](https://github.com/apache/paimon-rust) repository. It is intended 
for anyone participating in the release vote (binding or non-binding) and is 
based on [ASF Release 
Policy](https://www.apache.org/legal/release-policy.html), adapted for the 
paimon-rust source distribution and tooling.
+
+## Validating Distributions
+
+The release vote email includes links to:
+
+- **Distribution archive:** source tarball 
(`paimon-rust-${RELEASE_VERSION}.tar.gz`) on [dist.apache.org 
dev](https://dist.apache.org/repos/dist/dev/paimon/)
+- **Signature file:** `paimon-rust-${RELEASE_VERSION}.tar.gz.asc`
+- **Checksum file:** `paimon-rust-${RELEASE_VERSION}.tar.gz.sha512`
+- **KEYS file:** 
[https://downloads.apache.org/paimon/KEYS](https://downloads.apache.org/paimon/KEYS)
+
+Download the archive (`.tar.gz`), `.asc`, and `.sha512` from the RC directory 
(e.g. `paimon-rust-${RELEASE_VERSION}-rc${RC_NUM}/`) and the KEYS file. Then 
follow the steps below to verify signatures and checksums.
+
+## Verifying Signatures
+
+First, import the keys into your local keyring:
+
+```bash
+curl https://downloads.apache.org/paimon/KEYS -o KEYS
+gpg --import KEYS
+```
+
+Next, verify the `.asc` file:
+
+```bash
+gpg --verify paimon-rust-${RELEASE_VERSION}.tar.gz.asc 
paimon-rust-${RELEASE_VERSION}.tar.gz
+```
+
+If verification succeeds, you will see a message like:
+
+```text
+gpg: Signature made ...
+gpg: using RSA key ...
+gpg: Good signature from "Release Manager Name (CODE SIGNING KEY) 
<[email protected]>"
+```
+
+## Verifying Checksums
+
+Verify the tarball using the provided `.sha512` file. The `.sha512` file lists 
the expected SHA-512 hash for the corresponding archive; `-c` reads that file 
and checks the archive.
+
+**On macOS (shasum):**
+
+```bash
+shasum -a 512 -c paimon-rust-${RELEASE_VERSION}.tar.gz.sha512
+```
+
+**On Linux (sha512sum):**
+
+```bash
+sha512sum -c paimon-rust-${RELEASE_VERSION}.tar.gz.sha512
+```
+
+If the verification is successful, you will see a message like:
+
+```text
+paimon-rust-${RELEASE_VERSION}.tar.gz: OK
+```
+
+## Verifying Build
+
+Extract the source release archive and verify that it builds (and optionally 
that tests pass). You need **Rust** (see 
[rust-toolchain.toml](https://github.com/apache/paimon-rust/blob/main/rust-toolchain.toml)
 for the expected version).
+
+```bash
+tar -xzf paimon-rust-${RELEASE_VERSION}.tar.gz
+cd paimon-rust-${RELEASE_VERSION}
+```
+
+Build the workspace:
+
+```bash
+cargo build --workspace --release
+```
+
+For Python binding, see `bindings/python/`. For Go binding, see `bindings/go/`.
+
+## Verifying LICENSE and NOTICE
+
+Unzip the source release archive and verify that:
+
+1. The **LICENSE** and **NOTICE** files in the root directory are correct and 
refer to dependencies in the source release.
+2. All files that need it have ASF license headers.
+3. All dependencies have been checked for their license and the license is ASL 
2.0 compatible ([ASF third-party license 
policy](http://www.apache.org/legal/resolved.html#category-x)).
+4. Compatible non-ASL 2.0 licenses are documented (e.g. in NOTICE or in 
dependency audit files such as `DEPENDENCIES*.tsv`).
+
+The project uses [cargo-deny](https://embarkstudios.github.io/cargo-deny/) for 
license checks; see [Creating a Release](creating-a-release.md) for how the 
dependency list is generated before a release.
+
+## Testing Features
+
+For any user-facing feature included in a release, we aim to ensure it is 
functional, usable, and well-documented. Release managers may create testing 
issues that outline key scenarios to validate; these are open to all community 
members.
+
+**Per-component verification:**
+
+- **Rust crates:** You can depend on the RC via its git tag (e.g. in your 
`Cargo.toml`: `paimon = { git = "https://github.com/apache/paimon-rust";, tag = 
"v${RELEASE_VERSION}-rc${RC_NUM}" }`) and build your own test project to 
verify. Alternatively, build from the source release; see [Getting 
Started](https://paimon.apache.org/docs/rust/getting-started/) for usage 
examples.
+- **Python binding:** The RC is published to **TestPyPI**; install the client 
from TestPyPI and write your own test cases to verify:
+
+    ```bash
+    pip install -i https://test.pypi.org/simple/ pypaimon==${RELEASE_VERSION}
+    ```
+
+- **Go binding:** The RC is published as a Go module tag 
`bindings/go/v${RELEASE_VERSION}-rc${RC_NUM}`; see [Go 
Binding](https://paimon.apache.org/docs/rust/go-binding/) for usage. Add it to 
your Go project and write test cases to verify:
+
+    ```bash
+    go get 
github.com/apache/paimon-rust/bindings/go@v${RELEASE_VERSION}-rc${RC_NUM}
+    ```
+
+## Voting
+
+Votes are cast by replying to the vote email on the dev mailing list with 
**+1**, **0**, or **-1**.
+
+In addition to your vote, it is customary to state whether your vote is 
**binding** or **non-binding**. Only members of the PMC have formally binding 
votes. If unsure, you can state that your vote is non-binding. See [Apache 
Foundation Voting](https://www.apache.org/foundation/voting.html).
+
+It is recommended to include a short list of what you verified (e.g. 
signatures, checksums, build, tests, LICENSE/NOTICE). This helps the community 
see what has been checked and what might still be missing.
+
+**Checklist you can reference in your vote:**
+
+- [ ] [Validating distributions](#validating-distributions)
+- [ ] [Verifying signatures](#verifying-signatures)
+- [ ] [Verifying checksums](#verifying-checksums)
+- [ ] [Verifying build](#verifying-build)
+- [ ] [Verifying LICENSE and NOTICE](#verifying-license-and-notice)
+- [ ] [Testing features](#testing-features)
diff --git a/scripts/bump-version.sh b/scripts/bump-version.sh
new file mode 100755
index 0000000..49bea6c
--- /dev/null
+++ b/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]).
+# Run from repo root.
+#
+# Usage: ./scripts/bump-version.sh <current_version> <next_version>
+#   e.g. ./scripts/bump-version.sh 0.1.0 0.2.0
+
+set -e
+
+if [ -z "$1" ] || [ -z "$2" ]; then
+  echo "Usage: $0 <current_version> <next_version>"
+  echo "  e.g. $0 0.1.0 0.2.0"
+  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 only within [workspace.package] section to avoid accidentally
+# modifying third-party dependency versions in [workspace.dependencies].
+case "$(uname -s)" in
+  Darwin)
+    sed -i '' '/^\[workspace\.package\]/,/^\[/{s/version = 
"'"${FROM_VERSION}"'"/version = "'"${TO_VERSION}"'"/;}' Cargo.toml
+    ;;
+  *)
+    sed -i '/^\[workspace\.package\]/,/^\[/{s/version = 
"'"${FROM_VERSION}"'"/version = "'"${TO_VERSION}"'"/;}' Cargo.toml
+    ;;
+esac
+
+echo "Bumped version from ${FROM_VERSION} to ${TO_VERSION} in Cargo.toml"
+echo "Review with: git diff Cargo.toml"
diff --git a/scripts/constants.py b/scripts/constants.py
new file mode 100644
index 0000000..4a23e6a
--- /dev/null
+++ b/scripts/constants.py
@@ -0,0 +1,45 @@
+#!/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.
+    Requires Python 3.11+ (tomllib).
+    """
+    root_cargo = ROOT_DIR / "Cargo.toml"
+    if not root_cargo.exists():
+        return ["."]
+    with open(root_cargo, "rb") as f:
+        data = tomllib.load(f)
+    members = data.get("workspace", {}).get("members", [])
+    if not isinstance(members, list):
+        return ["."]
+    packages = ["."]
+    for m in members:
+        if isinstance(m, str) and m:
+            packages.append(m)
+    return packages
+
+
+PACKAGES = list_packages()
diff --git a/scripts/dependencies.py b/scripts/dependencies.py
new file mode 100644
index 0000000..ec77469
--- /dev/null
+++ b/scripts/dependencies.py
@@ -0,0 +1,96 @@
+#!/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.
+#
+# Release tooling: requires Python 3.11+ (constants.py uses tomllib).
+
+import sys
+
+if sys.version_info < (3, 11):
+    sys.exit(
+        "This script requires Python 3.11 or newer (uses tomllib). "
+        f"Current: {sys.version}. Use python3.11+ or see docs for release 
requirements."
+    )
+
+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,
+        )
+    else:
+        print(f"Skipping {root} as Cargo.toml does not exist")
+
+
+def check_deps():
+    for d in PACKAGES:
+        check_single_package(d)
+
+
+def generate_single_package(root):
+    pkg_dir = ROOT_DIR / root if root != "." else ROOT_DIR
+    if (pkg_dir / "Cargo.toml").exists():
+        print(f"Generating dependencies {root}")
+        result = subprocess.run(
+            ["cargo", "deny", "list", "-f", "tsv", "-t", "0.6"],
+            cwd=pkg_dir,
+            capture_output=True,
+            text=True,
+        )
+        if result.returncode != 0:
+            raise RuntimeError(
+                f"cargo deny list failed in {root}: {result.stderr or 
result.stdout}"
+            )
+        out_file = pkg_dir / "DEPENDENCIES.rust.tsv"
+        out_file.write_text(result.stdout)
+    else:
+        print(f"Skipping {root} as Cargo.toml does not exist")
+
+
+def generate_deps():
+    for d in PACKAGES:
+        generate_single_package(d)
+
+
+if __name__ == "__main__":
+    parser = ArgumentParser(formatter_class=ArgumentDefaultsHelpFormatter)
+    parser.set_defaults(func=parser.print_help)
+    subparsers = parser.add_subparsers()
+
+    parser_check = subparsers.add_parser(
+        "check", description="Check dependencies", help="Check dependencies"
+    )
+    parser_check.set_defaults(func=check_deps)
+
+    parser_generate = subparsers.add_parser(
+        "generate", description="Generate dependencies", help="Generate 
dependencies"
+    )
+    parser_generate.set_defaults(func=generate_deps)
+
+    args = parser.parse_args()
+    arg_dict = dict(vars(args))
+    del arg_dict["func"]
+    args.func(**arg_dict)
diff --git a/scripts/release.sh b/scripts/release.sh
new file mode 100755
index 0000000..6c95f08
--- /dev/null
+++ b/scripts/release.sh
@@ -0,0 +1,67 @@
+#!/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/:
+#   paimon-rust-{version}.tar.gz
+#   paimon-rust-{version}.tar.gz.asc
+#   paimon-rust-{version}.tar.gz.sha512
+#
+# Run from repo root. Check out the release tag first (e.g. git checkout 
v0.1.0-rc1).
+# 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="paimon-rust-${VERSION}"
+DIST_DIR="${REPO_ROOT}/dist"
+TARBALL="${PREFIX}.tar.gz"
+
+echo "Creating ASF source release for paimon-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 
docs/src/release/creating-a-release.md)."


Reply via email to