This is an automated email from the ASF dual-hosted git repository.
JingsongLi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/paimon-vector-index.git
The following commit(s) were added to refs/heads/main by this push:
new 054d736 Add CI release infrastructure for Java/Rust/Python (#39)
054d736 is described below
commit 054d7369114195d21d5a3abcb1b3f2d5ded6e26a
Author: jianguotian <[email protected]>
AuthorDate: Fri Jun 12 14:49:25 2026 +0800
Add CI release infrastructure for Java/Rust/Python (#39)
---
.github/workflows/publish_snapshot.yml | 198 +++++++++++++++++++++++++++
.github/workflows/release-java.yml | 179 ++++++++++++++++++++++++
.github/workflows/release-python-publish.yml | 89 ++++++++++++
.github/workflows/release-python.yml | 183 +++++++++++++++++++++++++
.github/workflows/release-rust.yml | 55 ++++++++
.github/workflows/release.yml | 59 ++++++++
deploysettings.xml | 37 +++++
java/pom.xml | 127 +++++++++++++++++
tools/create_release_branch.sh | 57 ++++++++
tools/create_source_release.sh | 90 ++++++++++++
tools/dependencies.py | 124 +++++++++++++++++
tools/update_branch_version.sh | 69 ++++++++++
12 files changed, 1267 insertions(+)
diff --git a/.github/workflows/publish_snapshot.yml
b/.github/workflows/publish_snapshot.yml
new file mode 100644
index 0000000..c5af5b6
--- /dev/null
+++ b/.github/workflows/publish_snapshot.yml
@@ -0,0 +1,198 @@
+################################################################################
+# 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.
+################################################################################
+
+name: Publish Snapshot
+
+on:
+ schedule:
+ # At the end of every day
+ - cron: '0 0 * * *'
+ workflow_dispatch:
+
+env:
+ JDK_VERSION: 8
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event_name }}-${{
github.event.number || github.run_id }}
+ cancel-in-progress: true
+
+jobs:
+ build-native:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: ubuntu-latest
+ target: x86_64-unknown-linux-gnu
+ os_name: linux
+ arch: x86_64
+ lib_name: libpaimon_vindex_jni.so
+ - os: ubuntu-24.04-arm
+ target: aarch64-unknown-linux-gnu
+ os_name: linux
+ arch: aarch64
+ lib_name: libpaimon_vindex_jni.so
+ - os: macos-latest
+ target: aarch64-apple-darwin
+ os_name: macos
+ arch: aarch64
+ lib_name: libpaimon_vindex_jni.dylib
+ - os: windows-latest
+ target: x86_64-pc-windows-msvc
+ os_name: windows
+ arch: x86_64
+ lib_name: paimon_vindex_jni.dll
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Setup Rust toolchain
+ run: |
+ rustup update stable
+ rustup default stable
+
+ - name: Cache Rust dependencies
+ uses: actions/cache@v5
+ with:
+ path: |
+ ~/.cargo/registry
+ ~/.cargo/git
+ target
+ key: ${{ runner.os }}-${{ matrix.target }}-cargo-${{
hashFiles('**/Cargo.lock') }}
+ restore-keys: |
+ ${{ runner.os }}-${{ matrix.target }}-cargo-
+
+ - name: Setup Python
+ if: matrix.os_name == 'linux'
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Setup Linux zigbuild
+ if: matrix.os_name == 'linux'
+ run: pip install cargo-zigbuild
+
+ - name: Build Linux JNI library
+ if: matrix.os_name == 'linux'
+ shell: bash
+ run: |
+ set -euo pipefail
+
+ rustup target add "${{ matrix.target }}"
+ unset ZSTD_SYS_USE_PKG_CONFIG
+
+ cargo zigbuild --release -p paimon-vindex-jni --target "${{
matrix.target }}.2.17"
+
+ artifact="target/${{ matrix.target }}/release/${{ matrix.lib_name }}"
+ test -f "$artifact"
+
+ version_info="$(readelf --version-info "$artifact")"
+ max_glibc="$(
+ printf '%s\n' "$version_info" \
+ | grep -Eo 'GLIBC_[0-9][0-9.]*' \
+ | sort -Vu \
+ | tail -n1 \
+ || true
+ )"
+ echo "Maximum required GLIBC version: ${max_glibc:-none}"
+
+ if [[ -n "$max_glibc" ]]; then
+ allowed_glibc="GLIBC_2.17"
+ highest_glibc="$(printf '%s\n%s\n' "$allowed_glibc" "$max_glibc" |
sort -Vu | tail -n1)"
+ if [[ "$highest_glibc" != "$allowed_glibc" ]]; then
+ echo "Linux GNU artifact requires $max_glibc, expected <=
$allowed_glibc" >&2
+ exit 1
+ fi
+ fi
+
+ - name: Build JNI library
+ if: matrix.os_name != 'linux'
+ run: |
+ rustup target add "${{ matrix.target }}"
+ cargo build --release -p paimon-vindex-jni --target "${{
matrix.target }}"
+
+ - name: Upload native library
+ uses: actions/upload-artifact@v5
+ with:
+ name: native-${{ matrix.os_name }}-${{ matrix.arch }}
+ path: target/${{ matrix.target }}/release/${{ matrix.lib_name }}
+
+ publish-snapshot:
+ if: github.repository == 'apache/paimon-vector-index'
+ runs-on: ubuntu-latest
+ needs: [build-native]
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Download linux x86_64 native library
+ uses: actions/download-artifact@v5
+ with:
+ name: native-linux-x86_64
+ path: java/src/main/resources/native/linux/x86_64
+
+ - name: Download linux aarch64 native library
+ uses: actions/download-artifact@v5
+ with:
+ name: native-linux-aarch64
+ path: java/src/main/resources/native/linux/aarch64
+
+ - name: Download macOS aarch64 native library
+ uses: actions/download-artifact@v5
+ with:
+ name: native-macos-aarch64
+ path: java/src/main/resources/native/macos/aarch64
+
+ - name: Download windows x86_64 native library
+ uses: actions/download-artifact@v5
+ with:
+ name: native-windows-x86_64
+ path: java/src/main/resources/native/windows/x86_64
+
+ - name: Verify native libraries
+ run: find java/src/main/resources/native -type f | sort
+
+ - name: Set up JDK ${{ env.JDK_VERSION }}
+ uses: actions/setup-java@v4
+ with:
+ java-version: ${{ env.JDK_VERSION }}
+ distribution: 'temurin'
+
+ - name: Cache local Maven repository
+ uses: actions/cache@v5
+ with:
+ path: ~/.m2/repository
+ key: snapshot-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ snapshot-maven-
+
+ - name: Publish snapshot
+ env:
+ ASF_USERNAME: ${{ secrets.NEXUS_USER }}
+ ASF_PASSWORD: ${{ secrets.NEXUS_PW }}
+ MAVEN_OPTS: -Xmx4096m
+ working-directory: java
+ run: |
+ tmp_settings="tmp-settings.xml"
+ echo "<settings><servers><server>" > $tmp_settings
+ echo
"<id>apache.snapshots.https</id><username>$ASF_USERNAME</username>" >>
$tmp_settings
+ echo "<password>$ASF_PASSWORD</password>" >> $tmp_settings
+ echo "</server></servers></settings>" >> $tmp_settings
+
+ mvn --settings $tmp_settings clean deploy -Dgpg.skip -Drat.skip
-DskipTests
+
+ rm $tmp_settings
diff --git a/.github/workflows/release-java.yml
b/.github/workflows/release-java.yml
new file mode 100644
index 0000000..17f85aa
--- /dev/null
+++ b/.github/workflows/release-java.yml
@@ -0,0 +1,179 @@
+################################################################################
+# 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.
+################################################################################
+
+name: Release Java
+
+on:
+ workflow_call:
+ workflow_dispatch:
+
+env:
+ JDK_VERSION: 8
+
+concurrency:
+ group: release-java-${{ github.ref }}
+ cancel-in-progress: false
+
+jobs:
+ build-native:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: ubuntu-latest
+ target: x86_64-unknown-linux-gnu
+ os_name: linux
+ arch: x86_64
+ lib_name: libpaimon_vindex_jni.so
+ - os: ubuntu-24.04-arm
+ target: aarch64-unknown-linux-gnu
+ os_name: linux
+ arch: aarch64
+ lib_name: libpaimon_vindex_jni.so
+ - os: macos-latest
+ target: aarch64-apple-darwin
+ os_name: macos
+ arch: aarch64
+ lib_name: libpaimon_vindex_jni.dylib
+ - os: windows-latest
+ target: x86_64-pc-windows-msvc
+ os_name: windows
+ arch: x86_64
+ lib_name: paimon_vindex_jni.dll
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Setup Rust toolchain
+ run: |
+ rustup update stable
+ rustup default stable
+
+ - name: Setup Python
+ if: matrix.os_name == 'linux'
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Setup Linux zigbuild
+ if: matrix.os_name == 'linux'
+ run: pip install cargo-zigbuild
+
+ - name: Build Linux JNI library
+ if: matrix.os_name == 'linux'
+ shell: bash
+ run: |
+ set -euo pipefail
+
+ rustup target add "${{ matrix.target }}"
+ unset ZSTD_SYS_USE_PKG_CONFIG
+
+ cargo zigbuild --release -p paimon-vindex-jni --target "${{
matrix.target }}.2.17"
+
+ artifact="target/${{ matrix.target }}/release/${{ matrix.lib_name }}"
+ test -f "$artifact"
+
+ version_info="$(readelf --version-info "$artifact")"
+ max_glibc="$(
+ printf '%s\n' "$version_info" \
+ | grep -Eo 'GLIBC_[0-9][0-9.]*' \
+ | sort -Vu \
+ | tail -n1 \
+ || true
+ )"
+ echo "Maximum required GLIBC version: ${max_glibc:-none}"
+
+ if [[ -n "$max_glibc" ]]; then
+ allowed_glibc="GLIBC_2.17"
+ highest_glibc="$(printf '%s\n%s\n' "$allowed_glibc" "$max_glibc" |
sort -Vu | tail -n1)"
+ if [[ "$highest_glibc" != "$allowed_glibc" ]]; then
+ echo "Linux GNU artifact requires $max_glibc, expected <=
$allowed_glibc" >&2
+ exit 1
+ fi
+ fi
+
+ - name: Build JNI library
+ if: matrix.os_name != 'linux'
+ run: |
+ rustup target add "${{ matrix.target }}"
+ cargo build --release -p paimon-vindex-jni --target "${{
matrix.target }}"
+
+ - name: Upload native library
+ uses: actions/upload-artifact@v5
+ with:
+ name: native-${{ matrix.os_name }}-${{ matrix.arch }}
+ path: target/${{ matrix.target }}/release/${{ matrix.lib_name }}
+
+ deploy-staging:
+ if: github.repository == 'apache/paimon-vector-index' &&
startsWith(github.ref, 'refs/tags/')
+ runs-on: ubuntu-latest
+ needs: [build-native]
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Download linux x86_64 native library
+ uses: actions/download-artifact@v5
+ with:
+ name: native-linux-x86_64
+ path: java/src/main/resources/native/linux/x86_64
+
+ - name: Download linux aarch64 native library
+ uses: actions/download-artifact@v5
+ with:
+ name: native-linux-aarch64
+ path: java/src/main/resources/native/linux/aarch64
+
+ - name: Download macOS aarch64 native library
+ uses: actions/download-artifact@v5
+ with:
+ name: native-macos-aarch64
+ path: java/src/main/resources/native/macos/aarch64
+
+ - name: Download windows x86_64 native library
+ uses: actions/download-artifact@v5
+ with:
+ name: native-windows-x86_64
+ path: java/src/main/resources/native/windows/x86_64
+
+ - name: Verify native libraries
+ run: find java/src/main/resources/native -type f | sort
+
+ - name: Set up JDK ${{ env.JDK_VERSION }}
+ uses: actions/setup-java@v4
+ with:
+ java-version: ${{ env.JDK_VERSION }}
+ distribution: 'temurin'
+ server-id: apache.releases.https
+ server-username: MAVEN_USERNAME
+ server-password: MAVEN_PASSWORD
+ gpg-private-key: ${{ secrets.GPG_SECRET_KEY }}
+ gpg-passphrase: MAVEN_GPG_PASSPHRASE
+
+ - name: Show Maven version
+ run: mvn --version
+
+ - name: Deploy to Apache Nexus staging
+ working-directory: java
+ run: |
+ mvn clean deploy \
+ -Prelease \
+ -DskipTests
+ env:
+ MAVEN_USERNAME: ${{ secrets.NEXUS_STAGE_DEPLOYER_USER }}
+ MAVEN_PASSWORD: ${{ secrets.NEXUS_STAGE_DEPLOYER_PW }}
+ MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
diff --git a/.github/workflows/release-python-publish.yml
b/.github/workflows/release-python-publish.yml
new file mode 100644
index 0000000..44b1cc8
--- /dev/null
+++ b/.github/workflows/release-python-publish.yml
@@ -0,0 +1,89 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Publish Python wheels after the orchestrator has confirmed the wheel
+# build completed successfully.
+
+name: Release Python Publish
+
+on:
+ workflow_call:
+
+concurrency:
+ group: release-python-publish-${{ github.ref }}
+ cancel-in-progress: false
+
+permissions:
+ actions: read
+ contents: read
+
+jobs:
+ publish:
+ name: Publish to PyPI
+ if: github.repository == 'apache/paimon-vector-index' &&
startsWith(github.ref, 'refs/tags/')
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/download-artifact@v5
+ with:
+ pattern: wheels-*
+ merge-multiple: true
+ path: dist
+
+ - name: Verify wheel versions
+ env:
+ TAG_NAME: ${{ github.ref_name }}
+ shell: bash
+ run: |
+ set -euo pipefail
+
+ expected_version="${TAG_NAME#v}"
+ expected_version="${expected_version/-rc/rc}"
+ shopt -s nullglob
+ wheels=(dist/*.whl)
+
+ if [[ "${#wheels[@]}" -eq 0 ]]; then
+ echo "No wheels found in dist" >&2
+ exit 1
+ fi
+
+ for wheel in "${wheels[@]}"; do
+ base="$(basename "$wheel")"
+ case "$base" in
+ paimon_vindex-"${expected_version}"-*.whl) ;;
+ *)
+ echo "Unexpected wheel for ${TAG_NAME}: ${base}" >&2
+ exit 1
+ ;;
+ esac
+ done
+
+ - name: Publish to TestPyPI
+ if: contains(github.ref_name, '-rc')
+ uses:
pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e
+ with:
+ repository-url: https://test.pypi.org/legacy/
+ skip-existing: true
+ packages-dir: dist
+ password: ${{ secrets.TEST_PYPI_API_TOKEN }}
+
+ - name: Publish to PyPI
+ if: ${{ !contains(github.ref_name, '-') }}
+ uses:
pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e
+ with:
+ skip-existing: true
+ packages-dir: dist
+ password: ${{ secrets.PYPI_API_TOKEN }}
diff --git a/.github/workflows/release-python.yml
b/.github/workflows/release-python.yml
new file mode 100644
index 0000000..39dc779
--- /dev/null
+++ b/.github/workflows/release-python.yml
@@ -0,0 +1,183 @@
+# 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.
+
+# Build the paimon-vindex Python release wheels.
+#
+# Trigger: called by release.yml or manually dispatched.
+# Publishing is handled by release-python-publish.yml after wheels are built.
+
+name: Release Python Wheels
+
+on:
+ workflow_call:
+ workflow_dispatch:
+
+concurrency:
+ group: release-python-${{ github.ref }}
+ cancel-in-progress: false
+
+permissions:
+ contents: read
+
+jobs:
+ wheels-linux:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: ubuntu-latest
+ arch: x86_64
+ - os: ubuntu-24.04-arm
+ arch: aarch64
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Inject RC version into pyproject.toml
+ if: startsWith(github.ref, 'refs/tags/') && contains(github.ref_name,
'-rc')
+ run: |
+ TAG="${GITHUB_REF#refs/tags/v}"
+ # Convert 0.1.0-rc1 to PEP 440: 0.1.0rc1
+ PEP440_VERSION=$(echo "$TAG" | sed 's/-rc/rc/')
+ sed -i "s/^version = .*/version = \"${PEP440_VERSION}\"/"
python/pyproject.toml
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Build wheels via cibuildwheel
+ uses: pypa/cibuildwheel@298ed2fb2c105540f5ed055e8a6ad78d82dd3a7e
+ with:
+ package-dir: python
+ output-dir: wheelhouse
+ env:
+ CIBW_BUILD: "cp39-manylinux_${{ matrix.arch }}"
+ CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28
+ CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28
+ CIBW_BEFORE_ALL_LINUX: >
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s
-- -y &&
+ source $HOME/.cargo/env &&
+ cargo build --release -p paimon-vindex-ffi &&
+ cp target/release/libpaimon_vindex_ffi.so {package}/paimon_vindex/
+
+ - name: Upload wheels
+ uses: actions/upload-artifact@v5
+ with:
+ name: wheels-linux-${{ matrix.arch }}
+ path: wheelhouse/*.whl
+
+ wheels-macos:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - os: macos-latest
+ target: aarch64-apple-darwin
+ lib_name: libpaimon_vindex_ffi.dylib
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Inject RC version into pyproject.toml
+ if: startsWith(github.ref, 'refs/tags/') && contains(github.ref_name,
'-rc')
+ run: |
+ TAG="${GITHUB_REF#refs/tags/v}"
+ PEP440_VERSION=$(echo "$TAG" | sed 's/-rc/rc/')
+ sed -i '' "s/^version = .*/version = \"${PEP440_VERSION}\"/"
python/pyproject.toml
+
+ - name: Setup Rust toolchain
+ run: |
+ rustup update stable
+ rustup default stable
+
+ - name: Build native library
+ run: cargo build --release -p paimon-vindex-ffi
+
+ - name: Copy native library into package
+ run: cp target/release/${{ matrix.lib_name }} python/paimon_vindex/
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Install build tools
+ run: pip install build wheel setuptools delocate
+
+ - name: Build wheel
+ working-directory: python
+ env:
+ _PYTHON_HOST_PLATFORM: macosx-11.0-arm64
+ ARCHFLAGS: "-arch arm64"
+ MACOSX_DEPLOYMENT_TARGET: "11.0"
+ run: python -m build --wheel
+
+ - name: Repair wheel
+ run: |
+ delocate-wheel --require-archs arm64 -w python/dist/repaired
python/dist/*.whl
+ rm python/dist/*.whl
+ mv python/dist/repaired/*.whl python/dist/
+
+ - name: Upload wheel
+ uses: actions/upload-artifact@v5
+ with:
+ name: wheels-${{ matrix.os }}-${{ matrix.target }}
+ path: python/dist/*.whl
+
+ wheels-windows:
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Inject RC version into pyproject.toml
+ if: startsWith(github.ref, 'refs/tags/') && contains(github.ref_name,
'-rc')
+ shell: bash
+ run: |
+ TAG="${GITHUB_REF#refs/tags/v}"
+ PEP440_VERSION=$(echo "$TAG" | sed 's/-rc/rc/')
+ sed -i "s/^version = .*/version = \"${PEP440_VERSION}\"/"
python/pyproject.toml
+
+ - name: Setup Rust toolchain
+ run: |
+ rustup update stable
+ rustup default stable
+
+ - name: Build native library
+ run: cargo build --release -p paimon-vindex-ffi
+
+ - name: Copy native library into package
+ shell: bash
+ run: cp target/release/paimon_vindex_ffi.dll python/paimon_vindex/
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Install build tools
+ run: pip install build wheel setuptools
+
+ - name: Build wheel
+ working-directory: python
+ run: python -m build --wheel
+
+ - name: Upload wheel
+ uses: actions/upload-artifact@v5
+ with:
+ name: wheels-windows-x86_64
+ path: python/dist/*.whl
diff --git a/.github/workflows/release-rust.yml
b/.github/workflows/release-rust.yml
new file mode 100644
index 0000000..2fd540c
--- /dev/null
+++ b/.github/workflows/release-rust.yml
@@ -0,0 +1,55 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# Publish paimon-vindex-core crate to crates.io.
+#
+# Trigger: called by release.yml or manually dispatched.
+# Pre-release tags (containing '-') only run dry-run checks without publishing.
+#
+# Token auth: add secret CARGO_REGISTRY_TOKEN for crates.io publishing.
+
+name: Release Rust
+
+on:
+ workflow_call:
+ workflow_dispatch:
+
+concurrency:
+ group: release-rust-${{ github.ref }}
+ cancel-in-progress: false
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Setup Rust toolchain
+ run: |
+ rustup update stable
+ rustup default stable
+
+ - name: Dry run
+ run: cargo publish -p paimon-vindex-core --dry-run
+
+ - name: Publish paimon-vindex-core to crates.io
+ if: github.repository == 'apache/paimon-vector-index' &&
startsWith(github.ref, 'refs/tags/') && !contains(github.ref_name, '-')
+ run: cargo publish -p paimon-vindex-core
+ env:
+ CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..4fd2491
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,59 @@
+# 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.
+
+# Orchestrate the full release while keeping each language release workflow
+# independently runnable through workflow_dispatch.
+
+name: Release
+
+on:
+ push:
+ tags:
+ - "v[0-9]+.[0-9]+.[0-9]+"
+ - "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+"
+ workflow_dispatch:
+
+concurrency:
+ group: release-${{ github.ref }}
+ cancel-in-progress: false
+
+permissions:
+ actions: read
+ contents: read
+
+jobs:
+ rust:
+ name: Rust release
+ uses: ./.github/workflows/release-rust.yml
+ secrets: inherit
+
+ java:
+ name: Java release
+ uses: ./.github/workflows/release-java.yml
+ secrets: inherit
+
+ python-wheels:
+ name: Python wheels
+ uses: ./.github/workflows/release-python.yml
+ secrets: inherit
+
+ python-publish:
+ name: Python publish
+ needs: [python-wheels]
+ if: startsWith(github.ref, 'refs/tags/')
+ uses: ./.github/workflows/release-python-publish.yml
+ secrets: inherit
diff --git a/deploysettings.xml b/deploysettings.xml
new file mode 100644
index 0000000..6026958
--- /dev/null
+++ b/deploysettings.xml
@@ -0,0 +1,37 @@
+<!--
+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.
+
+-->
+
+<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
+ http://maven.apache.org/xsd/settings-1.0.0.xsd">
+ <servers>
+ <server>
+ <id>apache.snapshots.https</id>
+ <username>${sonatype_user}</username>
+ <password>${sonatype_pw}</password>
+ </server>
+ <server>
+ <id>apache.releases.https</id>
+ <username>${sonatype_user}</username>
+ <password>${sonatype_pw}</password>
+ </server>
+ </servers>
+</settings>
diff --git a/java/pom.xml b/java/pom.xml
index d649d49..a2b7008 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -22,17 +22,51 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache</groupId>
+ <artifactId>apache</artifactId>
+ <version>23</version>
+ </parent>
+
<groupId>org.apache.paimon</groupId>
<artifactId>paimon-vector-index-java</artifactId>
<version>0.1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Apache Paimon Vector Index Java</name>
+ <description>Vector index for Java (backed by Rust via JNI)</description>
+ <url>https://paimon.apache.org</url>
+ <inceptionYear>2026</inceptionYear>
+
+ <licenses>
+ <license>
+ <name>The Apache Software License, Version 2.0</name>
+ <url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+
+ <scm>
+ <url>https://github.com/apache/paimon-vector-index</url>
+ <connection>[email protected]:apache/paimon-vector-index.git</connection>
+
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/paimon-vector-index.git</developerConnection>
+ </scm>
+
+ <developers>
+ <developer>
+ <name>Apache Paimon Contributors</name>
+ <email>[email protected]</email>
+ <organization>Apache Software Foundation</organization>
+ <organizationUrl>https://www.apache.org</organizationUrl>
+ </developer>
+ </developers>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <!-- Used by maven-remote-resources-plugin when generating
META-INF/NOTICE. -->
+
<project.build.outputTimestamp>2026-01-01T00:00:00Z</project.build.outputTimestamp>
</properties>
<build>
@@ -67,4 +101,97 @@
</plugin>
</plugins>
</build>
+
+ <profiles>
+ <profile>
+ <id>release</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-gpg-plugin</artifactId>
+ <version>3.2.8</version>
+ <executions>
+ <execution>
+ <id>sign-artifacts</id>
+ <phase>verify</phase>
+ <goals>
+ <goal>sign</goal>
+ </goals>
+ <configuration>
+ <bestPractices>true</bestPractices>
+ <gpgArguments>
+ <arg>--pinentry-mode</arg>
+ <arg>loopback</arg>
+ </gpgArguments>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>3.3.1</version>
+ <executions>
+ <execution>
+ <id>attach-sources</id>
+ <goals>
+ <goal>jar-no-fork</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>3.6.3</version>
+ <configuration>
+ <quiet>true</quiet>
+ </configuration>
+ <executions>
+ <execution>
+ <id>attach-javadocs</id>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>enforce-maven</id>
+ <goals>
+ <goal>enforce</goal>
+ </goals>
+ <configuration>
+ <rules>
+ <requireJavaVersion>
+ <version>1.8.0</version>
+ </requireJavaVersion>
+ <requireMavenVersion>
+ <version>[3.1.1,)</version>
+ </requireMavenVersion>
+ </rules>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.sonatype.plugins</groupId>
+ <artifactId>nexus-staging-maven-plugin</artifactId>
+ <version>1.7.0</version>
+ <extensions>true</extensions>
+ <configuration>
+ <serverId>apache.releases.https</serverId>
+ <nexusUrl>https://repository.apache.org/</nexusUrl>
+
<autoReleaseAfterClose>false</autoReleaseAfterClose>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
</project>
diff --git a/tools/create_release_branch.sh b/tools/create_release_branch.sh
new file mode 100755
index 0000000..71fd87c
--- /dev/null
+++ b/tools/create_release_branch.sh
@@ -0,0 +1,57 @@
+#!/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.
+#
+
+##
+## Variables with defaults (if not overwritten by environment)
+##
+RELEASE_CANDIDATE=${RELEASE_CANDIDATE:-none}
+MVN=${MVN:-mvn}
+
+# fail immediately
+set -o errexit
+set -o nounset
+# print command before executing
+set -o xtrace
+
+CURR_DIR=`pwd`
+if [[ `basename ${CURR_DIR}` != "tools" ]] ; then
+ echo "You have to call the script from the tools/ dir"
+ exit 1
+fi
+
+###########################
+
+if [ -z "${RELEASE_VERSION}" ]; then
+ echo "RELEASE_VERSION is unset"
+ exit 1
+fi
+
+cd ..
+
+target_branch=release-${RELEASE_VERSION}
+if [ "${RELEASE_CANDIDATE}" != "none" ]; then
+ target_branch=${target_branch}-rc${RELEASE_CANDIDATE}
+fi
+
+git checkout -b ${target_branch}
+
+RELEASE_HASH=`git rev-parse HEAD`
+echo "Echo created release hash $RELEASE_HASH"
+
+echo "Done. Don't forget to create the release tag on GitHub and push the
changes."
diff --git a/tools/create_source_release.sh b/tools/create_source_release.sh
new file mode 100755
index 0000000..cc04991
--- /dev/null
+++ b/tools/create_source_release.sh
@@ -0,0 +1,90 @@
+#!/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 tools/release/:
+# apache-paimon-vector-index-{version}-src.tgz
+# apache-paimon-vector-index-{version}-src.tgz.asc
+# apache-paimon-vector-index-{version}-src.tgz.sha512
+#
+# Usage: cd tools && RELEASE_VERSION=0.1.0 ./create_source_release.sh
+
+##
+## Variables with defaults (if not overwritten by environment)
+##
+MVN=${MVN:-mvn}
+
+# fail immediately
+set -o errexit
+set -o nounset
+set -o pipefail
+# print command before executing
+set -o xtrace
+
+CURR_DIR=`pwd`
+if [[ `basename $CURR_DIR` != "tools" ]] ; then
+ echo "You have to call the script from the tools/ dir"
+ exit 1
+fi
+
+if [ "$(uname)" == "Darwin" ]; then
+ SHASUM="shasum -a 512"
+else
+ SHASUM="sha512sum"
+fi
+
+###########################
+
+RELEASE_VERSION=${RELEASE_VERSION}
+
+if [ -z "${RELEASE_VERSION}" ]; then
+ echo "RELEASE_VERSION is unset"
+ exit 1
+fi
+
+rm -rf release
+mkdir release
+cd ..
+
+echo "Creating source package"
+
+ARCHIVE="apache-paimon-vector-index-${RELEASE_VERSION}-src.tgz"
+# Archive from Git objects so filesystem metadata such as macOS xattrs is not
included.
+git archive --format=tar --prefix="paimon-vector-index-${RELEASE_VERSION}/"
'HEAD^{tree}' . \
+ ':(exclude).gitignore' ':(exclude).gitattributes' \
+ ':(exclude).asf.yaml' ':(exclude).github' \
+ ':(exclude)deploysettings.xml' ':(exclude)target' \
+ ':(exclude).idea' ':(exclude)*.iml' ':(exclude).DS_Store' \
+ | gzip -n > "tools/release/${ARCHIVE}"
+
+cd tools/release
+
+gpg --armor --detach-sig "${ARCHIVE}"
+$SHASUM "${ARCHIVE}" > "${ARCHIVE}.sha512"
+
+echo "Verifying GPG signature"
+gpg --verify "${ARCHIVE}.asc" "${ARCHIVE}"
+
+echo "Verifying tarball integrity"
+tar tzf "${ARCHIVE}" > /dev/null
+
+echo ""
+echo "Source release created successfully. Artifacts in tools/release/:"
+ls -la ${CURR_DIR}/release/apache-paimon-vector-index-*
+echo ""
+echo "Next: upload contents to SVN (see docs/creating-a-release.html)."
diff --git a/tools/dependencies.py b/tools/dependencies.py
new file mode 100755
index 0000000..a4f7be1
--- /dev/null
+++ b/tools/dependencies.py
@@ -0,0 +1,124 @@
+#!/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.
+#
+
+"""Check and generate Rust dependency license information for ASF release
compliance.
+
+Requires cargo-deny: cargo install cargo-deny
+Requires Python 3.11+ (uses tomllib).
+
+Usage:
+ python3 tools/dependencies.py check # Verify all deps have approved
licenses
+ python3 tools/dependencies.py generate # Generate DEPENDENCIES.rust.tsv
+"""
+
+import sys
+
+if sys.version_info < (3, 11):
+ sys.exit(
+ "This script requires Python 3.11 or newer (uses tomllib). "
+ f"Current: {sys.version}."
+ )
+
+import subprocess
+import tomllib
+from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
+from pathlib import Path
+
+ROOT_DIR = Path(__file__).resolve().parent.parent
+
+PACKAGES = ["."]
+root_cargo = ROOT_DIR / "Cargo.toml"
+if root_cargo.exists():
+ with open(root_cargo, "rb") as f:
+ data = tomllib.load(f)
+ members = data.get("workspace", {}).get("members", [])
+ if isinstance(members, list):
+ for m in members:
+ if isinstance(m, str) and m:
+ PACKAGES.append(m)
+
+
+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 for {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)
+ print(f" Written to {out_file}")
+ 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(
+ description="Check and generate Rust dependency license information",
+ 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 dependency
licenses"
+ )
+ parser_check.set_defaults(func=check_deps)
+
+ parser_generate = subparsers.add_parser(
+ "generate",
+ description="Generate dependencies",
+ help="Generate DEPENDENCIES.rust.tsv",
+ )
+ 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/tools/update_branch_version.sh b/tools/update_branch_version.sh
new file mode 100755
index 0000000..03ec8a8
--- /dev/null
+++ b/tools/update_branch_version.sh
@@ -0,0 +1,69 @@
+#!/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.
+#
+
+##
+## Variables with defaults (if not overwritten by environment)
+##
+MVN=${MVN:-mvn}
+
+# fail immediately
+set -o errexit
+set -o nounset
+# print command before executing
+set -o xtrace
+
+CURR_DIR=`pwd`
+if [[ `basename $CURR_DIR` != "tools" ]] ; then
+ echo "You have to call the script from the tools/ dir"
+ exit 1
+fi
+
+###########################
+
+OLD_VERSION=${OLD_VERSION}
+NEW_VERSION=${NEW_VERSION}
+
+
+if [ -z "${OLD_VERSION}" ]; then
+ echo "OLD_VERSION is unset"
+ exit 1
+fi
+
+if [ -z "${NEW_VERSION}" ]; then
+ echo "NEW_VERSION is unset"
+ exit 1
+fi
+
+cd ..
+
+# For Cargo.toml and pyproject.toml, strip any -SNAPSHOT suffix (not valid in
those ecosystems)
+NEW_VERSION_CLEAN=$(echo "$NEW_VERSION" | sed 's/-SNAPSHOT//')
+
+#change version in all pom files (match both exact and -SNAPSHOT suffix)
+find . -name 'pom.xml' -type f -exec perl -pi -e
's#<version>'$OLD_VERSION'(-SNAPSHOT)?</version>#<version>'$NEW_VERSION'</version>#'
{} \;
+
+#change version in Cargo.toml files
+find . -name 'Cargo.toml' -not -path '*/target/*' -type f -exec perl -pi -e
's#^version = "'$OLD_VERSION'"#version = "'$NEW_VERSION_CLEAN'"#' {} \;
+
+#change version in pyproject.toml
+perl -pi -e 's#^version = "'$OLD_VERSION'"#version = "'$NEW_VERSION_CLEAN'"#'
python/pyproject.toml
+
+git commit -am "Update version to $NEW_VERSION"
+
+echo "Don't forget to push the change."