diff --git a/.licenserc.yaml b/.licenserc.yaml index c9e97553..a8d27a43 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 cfba61f4..3a4ddabe 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 f5a777ba..79131e55 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 00000000..832e4e08 --- /dev/null +++ b/bindings/go/DEPENDENCIES.md @@ -0,0 +1,23 @@ + + +# 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 00000000..e69de29b diff --git a/bindings/python/NOTICE b/bindings/python/NOTICE index f5a777ba..79131e55 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 02dd81be..98a4dec0 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 37d95e20..dc4ee1ca 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -50,8 +50,11 @@ nav: - Getting Started: getting-started.md - DataFusion Integration: datafusion.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 824d1f30..18930b64 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 7763fea8..3e5df5dc 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 00000000..6b2ae7c2 --- /dev/null +++ b/docs/src/release/creating-a-release.md @@ -0,0 +1,393 @@ + + +# 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/list.html?dev@paimon.apache.org), 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 + sub rsa4096/12345678 2024-01-01 [E] + ``` + + In this example, the key ID is `845E6689`. Replace `` 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 + ``` + +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 && gpg --armor --export ) >> KEYS + svn ci -m "Add '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 + ``` + + 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 `dev@paimon.apache.org` and `announce@apache.org` 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 00000000..bf7602dd 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 00000000..424e8504 --- /dev/null +++ b/docs/src/release/verifying-a-release-candidate.md @@ -0,0 +1,142 @@ + + +# 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) <...@apache.org>" +``` + +## 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 00000000..49bea6c4 --- /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 +# e.g. ./scripts/bump-version.sh 0.1.0 0.2.0 + +set -e + +if [ -z "$1" ] || [ -z "$2" ]; then + echo "Usage: $0 " + 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 00000000..4a23e6a5 --- /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 00000000..ec77469f --- /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 00000000..6c95f089 --- /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 " + 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)."