Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .asf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ github:
branches:
includes:
- "~DEFAULT_BRANCH"
- "release/*"
- "release-*"
excludes: []
bypass_teams:
- root
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ jobs:
tools/convert_ann_benchmarks.py \
tools/verify_binary_artifact.py

- name: Test release helper scripts
run: python3 -m unittest discover -s tools/tests -p 'test_*.py' -v

- name: Validate .asf.yaml
run: pip install pyyaml -q && python3 tools/validate_asf_yaml.py

Expand Down
6 changes: 3 additions & 3 deletions DEPENDENCIES.rust.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ num-rational@0.4.2 X X
num-traits@0.2.19 X X
once_cell@1.21.4 X X
once_cell_polyfill@1.70.2 X X
paimon-vindex-core@0.3.0 X
paimon-vindex-ffi@0.3.0 X
paimon-vindex-jni@0.3.0 X
paimon-vindex-core@0.4.0 X
paimon-vindex-ffi@0.4.0 X
paimon-vindex-jni@0.4.0 X
paste@1.0.15 X X
ppv-lite86@0.2.21 X X
proc-macro2@1.0.106 X X
Expand Down
2 changes: 1 addition & 1 deletion core/DEPENDENCIES.rust.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ num-complex@0.4.6 X X
num-integer@0.1.46 X X
num-rational@0.4.2 X X
num-traits@0.2.19 X X
paimon-vindex-core@0.3.0 X
paimon-vindex-core@0.4.0 X
paste@1.0.15 X X
ppv-lite86@0.2.21 X X
proc-macro2@1.0.106 X X
Expand Down
12 changes: 9 additions & 3 deletions docs/releases.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ <h1>Releases</h1>
</section>

<div class="doc-layout">
<aside class="toc" aria-label="On this page"><strong>On this page</strong><a href="#channels">Distribution channels</a><a href="#upcoming">Upcoming</a><a href="#release-020">0.2.0</a><a href="#release-010">0.1.0</a><a href="#guides">Release guides</a></aside>
<aside class="toc" aria-label="On this page"><strong>On this page</strong><a href="#channels">Distribution channels</a><a href="#upcoming">Upcoming</a><a href="#release-030">0.3.0</a><a href="#release-020">0.2.0</a><a href="#release-010">0.1.0</a><a href="#guides">Release guides</a></aside>
<article class="article">
<section class="article-section" id="channels">
<h2>Distribution channels</h2>
Expand All @@ -47,9 +47,15 @@ <h2>Distribution channels</h2>
</section>

<section class="article-section" id="upcoming">
<h2>Upcoming: 0.3.0</h2>
<p>The repository is currently developing the 0.3.0 line. Until an ASF vote passes and the signed source archive appears under Apache downloads, code and packages from this line are development artifacts rather than an Apache release.</p>
<h2>Upcoming: 0.4.0</h2>
<p>The repository is currently developing the 0.4.0 line. The planned release improves filtered and batched IVF scans, stabilizes IVF-PQ training, adds extensible search parameters across language bindings, bridges native diagnostics to Java logging, and tightens serialization allocation bounds. Until an ASF vote passes and the signed source archive appears under Apache downloads, code and packages from this line are development artifacts rather than an Apache release.</p>
</section>

<section class="article-section" id="release-030">
<h2>0.3.0</h2>
<p>Released 30 July 2026. This release added native DiskANN and adaptive IVF indexes, IVF-RQ query-width controls and search optimizations, batched positional reads across C and Python, expanded architecture and API documentation, and reproducible source and multi-platform convenience-artifact release verification.</p>
<div class="callout warning"><strong>Stored-index upgrade note</strong>Version 0.3.0 intentionally does not read the experimental IVF-HNSW-FLAT (<code>IHFL</code>) and IVF-HNSW-SQ (<code>IHSQ</code>) files written by 0.2.x. Rebuild those indexes with a 0.3.0-supported index type during upgrade. Index files written by 0.3.0 are likewise not a safe rollback boundary for 0.2.x; retain source vectors or a 0.2-compatible index copy until the upgrade is accepted.</div>
<p><a href="https://www.apache.org/dyn/closer.lua/paimon/paimon-vector-index-0.3.0/apache-paimon-vector-index-0.3.0-src.tgz?action=download">Source release</a> · <a href="https://github.com/apache/paimon-vector-index/releases/tag/v0.3.0">Release notes</a> · <a href="https://crates.io/crates/paimon-vindex-core/0.3.0">Rust crate</a> · <a href="https://repo.maven.apache.org/maven2/org/apache/paimon/paimon-vector-index-java/0.3.0/">Java artifacts</a> · <a href="https://pypi.org/project/paimon-vindex/0.3.0/">Python package</a></p>
</section>

<section class="article-section" id="release-020">
Expand Down
4 changes: 2 additions & 2 deletions ffi/DEPENDENCIES.rust.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ num-rational@0.4.2 X X
num-traits@0.2.19 X X
once_cell@1.21.4 X X
once_cell_polyfill@1.70.2 X X
paimon-vindex-core@0.3.0 X
paimon-vindex-ffi@0.3.0 X
paimon-vindex-core@0.4.0 X
paimon-vindex-ffi@0.4.0 X
paste@1.0.15 X X
ppv-lite86@0.2.21 X X
proc-macro2@1.0.106 X X
Expand Down
4 changes: 2 additions & 2 deletions jni/DEPENDENCIES.rust.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ num-complex@0.4.6 X X
num-integer@0.1.46 X X
num-rational@0.4.2 X X
num-traits@0.2.19 X X
paimon-vindex-core@0.3.0 X
paimon-vindex-jni@0.3.0 X
paimon-vindex-core@0.4.0 X
paimon-vindex-jni@0.4.0 X
paste@1.0.15 X X
ppv-lite86@0.2.21 X X
proc-macro2@1.0.106 X X
Expand Down
8 changes: 5 additions & 3 deletions tools/create_release_branch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ MVN=${MVN:-mvn}
# fail immediately
set -o errexit
set -o nounset
set -o pipefail
# print command before executing
set -o xtrace

Expand All @@ -37,19 +38,20 @@ fi

###########################

if [ -z "${RELEASE_VERSION}" ]; then
if [ -z "${RELEASE_VERSION:-}" ]; then
echo "RELEASE_VERSION is unset"
exit 1
fi

cd ..

target_branch=release-${RELEASE_VERSION}
SHORT_RELEASE_VERSION=${RELEASE_VERSION%.*}
target_branch=release-${SHORT_RELEASE_VERSION}
if [ "${RELEASE_CANDIDATE}" != "none" ]; then
target_branch=${target_branch}-rc${RELEASE_CANDIDATE}
fi

git checkout -b ${target_branch}
git checkout -b "${target_branch}"

RELEASE_HASH=`git rev-parse HEAD`
echo "Echo created release hash $RELEASE_HASH"
Expand Down
252 changes: 252 additions & 0 deletions tools/tests/test_release_scripts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
# 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 os
import shutil
import subprocess
import tempfile
import unittest
from pathlib import Path


REPO_ROOT = Path(__file__).resolve().parents[2]


class ReleaseScriptTest(unittest.TestCase):
def setUp(self):
self.temp_dir = tempfile.TemporaryDirectory()
self.repo = Path(self.temp_dir.name)
(self.repo / "tools").mkdir()
(self.repo / "core").mkdir()
(self.repo / "ffi").mkdir()
(self.repo / "jni").mkdir()
(self.repo / "python").mkdir()
(self.repo / "java").mkdir()

shutil.copy(
REPO_ROOT / "tools" / "update_branch_version.sh",
self.repo / "tools" / "update_branch_version.sh",
)
shutil.copy(
REPO_ROOT / "tools" / "create_release_branch.sh",
self.repo / "tools" / "create_release_branch.sh",
)

self._write(
"Cargo.toml",
"""
[workspace]
members = ["core", "ffi", "jni"]
resolver = "2"
""",
)
self._write(".gitignore", "/target/\n")
self._write(
"core/Cargo.toml",
"""
[package]
name = "paimon-vindex-core"
version = "0.4.0"
edition = "2021"
""",
)
self._write("core/src/lib.rs", "")
self._write(
"ffi/Cargo.toml",
"""
[package]
name = "paimon-vindex-ffi"
version = "0.4.0"
edition = "2021"

[dependencies]
paimon-vindex-core = { path = "../core", version = "0.4.0" }
""",
)
self._write("ffi/src/lib.rs", "")
self._write(
"jni/Cargo.toml",
"""
[package]
name = "paimon-vindex-jni"
version = "0.4.0"
edition = "2021"

[dependencies]
paimon-vindex-core = { path = "../core", version = "0.4.0" }
""",
)
self._write("jni/src/lib.rs", "")
self._write(
"python/pyproject.toml",
"""
[project]
name = "paimon-vindex"
version = "0.4.0"
""",
)
self._write(
"java/pom.xml",
"""
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.apache.paimon</groupId>
<artifactId>paimon-vector-index-java</artifactId>
<version>0.4.0-SNAPSHOT</version>
</project>
""",
)

self._run(["git", "init", "-q"])
self._run(["git", "config", "user.name", "Release Script Test"])
self._run(["git", "config", "user.email", "release-script@example.com"])
self._run(["cargo", "generate-lockfile"])
self._run(["git", "add", "."])
self._run(["git", "commit", "-q", "-m", "initial"])

def tearDown(self):
self.temp_dir.cleanup()

def test_update_branch_version_keeps_workspace_versions_consistent(self):
env = os.environ.copy()
env.update(
{
"OLD_VERSION": "0.4.0",
"NEW_VERSION": "0.5.0-SNAPSHOT",
}
)
self._run(
["bash", "update_branch_version.sh"],
cwd=self.repo / "tools",
env=env,
)

self.assertFalse(
(self.repo / "target").exists(),
"version updates should not compile the workspace",
)
self.assertIn(
'version = "0.5.0"',
(self.repo / "core" / "Cargo.toml").read_text(),
)
for crate in ("ffi", "jni"):
manifest = (self.repo / crate / "Cargo.toml").read_text()
self.assertIn('version = "0.5.0"', manifest)
self.assertIn(
'paimon-vindex-core = { path = "../core", version = "0.5.0" }',
manifest,
)
self.assertIn(
'version = "0.5.0"',
(self.repo / "python" / "pyproject.toml").read_text(),
)
self.assertIn(
"<version>0.5.0-SNAPSHOT</version>",
(self.repo / "java" / "pom.xml").read_text(),
)

lockfile = (self.repo / "Cargo.lock").read_text()
for package in (
"paimon-vindex-core",
"paimon-vindex-ffi",
"paimon-vindex-jni",
):
self.assertIn(
f'name = "{package}"\nversion = "0.5.0"',
lockfile,
)

subject = self._run(
["git", "log", "-1", "--format=%s"],
capture_output=True,
).stdout.strip()
self.assertEqual("Update version to 0.5.0-SNAPSHOT", subject)
self.assertEqual(
"",
self._run(
["git", "status", "--short"],
capture_output=True,
).stdout.strip(),
)

def test_create_release_branch_uses_minor_release_line(self):
env = os.environ.copy()
env["RELEASE_VERSION"] = "0.4.0"
self._run(
["bash", "create_release_branch.sh"],
cwd=self.repo / "tools",
env=env,
)

branch = self._run(
["git", "branch", "--show-current"],
capture_output=True,
).stdout.strip()
self.assertEqual("release-0.4", branch)

def test_update_branch_version_reports_missing_old_version(self):
env = os.environ.copy()
env.pop("OLD_VERSION", None)
env["NEW_VERSION"] = "0.5.0-SNAPSHOT"
result = self._run(
["bash", "update_branch_version.sh"],
cwd=self.repo / "tools",
env=env,
check=False,
capture_output=True,
)

self.assertNotEqual(0, result.returncode)
self.assertIn(
"OLD_VERSION is unset",
result.stdout + result.stderr,
)

def test_create_release_branch_reports_missing_release_version(self):
env = os.environ.copy()
env.pop("RELEASE_VERSION", None)
result = self._run(
["bash", "create_release_branch.sh"],
cwd=self.repo / "tools",
env=env,
check=False,
capture_output=True,
)

self.assertNotEqual(0, result.returncode)
self.assertIn(
"RELEASE_VERSION is unset",
result.stdout + result.stderr,
)

def _write(self, relative_path, content):
path = self.repo / relative_path
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content.lstrip())

def _run(self, command, cwd=None, env=None, check=True, capture_output=False):
return subprocess.run(
command,
cwd=cwd or self.repo,
env=env,
check=check,
text=True,
capture_output=capture_output,
)


if __name__ == "__main__":
unittest.main()
Loading
Loading