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
+
+
+
+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)."