This is an automated email from the ASF dual-hosted git repository.
XiaoHongbo-Hope pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/paimon-mosaic.git
The following commit(s) were added to refs/heads/main by this push:
new 2824a43 ci: publish Python release after Rust and Java (#42)
2824a43 is described below
commit 2824a434b5602a8394a1e0a15afaf71bdae9132b
Author: QuakeWang <[email protected]>
AuthorDate: Sun May 24 16:14:34 2026 +0800
ci: publish Python release after Rust and Java (#42)
Signed-off-by: QuakeWang <[email protected]>
---
.github/workflows/release-java.yml | 9 +--
.github/workflows/release-python-publish.yml | 89 ++++++++++++++++++++++
.github/workflows/release-python.yml | 57 +++-----------
.github/workflows/release-rust.yml | 13 ++--
.../workflows/{release-rust.yml => release.yml} | 59 +++++++-------
docs/creating-a-release.html | 24 +++---
docs/verifying-a-release-candidate.html | 2 +-
7 files changed, 156 insertions(+), 97 deletions(-)
diff --git a/.github/workflows/release-java.yml
b/.github/workflows/release-java.yml
index 519b5b2..961ba7d 100644
--- a/.github/workflows/release-java.yml
+++ b/.github/workflows/release-java.yml
@@ -19,18 +19,15 @@
name: Release Java
on:
- push:
- tags:
- - "v[0-9]+.[0-9]+.[0-9]+"
- - "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+"
+ workflow_call:
workflow_dispatch:
env:
JDK_VERSION: 8
concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
+ group: release-java-${{ github.ref }}
+ cancel-in-progress: false
jobs:
build-native:
diff --git a/.github/workflows/release-python-publish.yml
b/.github/workflows/release-python-publish.yml
new file mode 100644
index 0000000..34cab74
--- /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 Rust and Java
+# release jobs 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-mosaic' && 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_mosaic-"${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
index 1b103d0..257f04d 100644
--- a/.github/workflows/release-python.yml
+++ b/.github/workflows/release-python.yml
@@ -15,25 +15,21 @@
# specific language governing permissions and limitations
# under the License.
-# Publish the paimon-mosaic Python package to PyPI.
+# Build the paimon-mosaic Python release wheels.
#
-# Trigger: push a version tag (e.g. v0.1.0, v0.1.0-rc1).
-# Pre-release tags (containing '-') publish to TestPyPI; release tags publish
to PyPI.
-#
-# Token auth: add secrets PYPI_API_TOKEN / TEST_PYPI_API_TOKEN for publishing.
+# Trigger: called by release.yml or manually dispatched.
+# Publishing is handled by release-python-publish.yml after Rust and Java
+# release jobs complete successfully.
-name: Release Python
+name: Release Python Wheels
on:
- push:
- tags:
- - "v[0-9]+.[0-9]+.[0-9]+"
- - "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+"
+ workflow_call:
workflow_dispatch:
concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
- cancel-in-progress: true
+ group: release-python-${{ github.ref }}
+ cancel-in-progress: false
permissions:
contents: read
@@ -53,7 +49,7 @@ jobs:
- uses: actions/checkout@v6
- name: Inject RC version into pyproject.toml
- if: contains(github.ref, '-rc')
+ 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
@@ -99,7 +95,7 @@ jobs:
- uses: actions/checkout@v6
- name: Inject RC version into pyproject.toml
- if: contains(github.ref, '-rc')
+ 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/')
@@ -150,7 +146,7 @@ jobs:
- uses: actions/checkout@v6
- name: Inject RC version into pyproject.toml
- if: contains(github.ref, '-rc')
+ if: startsWith(github.ref, 'refs/tags/') && contains(github.ref_name,
'-rc')
shell: bash
run: |
TAG="${GITHUB_REF#refs/tags/v}"
@@ -186,34 +182,3 @@ jobs:
with:
name: wheels-windows-x86_64
path: python/dist/*.whl
-
- release:
- name: Publish to PyPI
- runs-on: ubuntu-latest
- permissions:
- contents: read
- needs: [wheels-linux, wheels-macos, wheels-windows]
- if: startsWith(github.ref, 'refs/tags/')
- steps:
- - uses: actions/download-artifact@v5
- with:
- pattern: wheels-*
- merge-multiple: true
- path: dist
-
- - name: Publish to TestPyPI
- if: contains(github.ref, '-')
- 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, '-') }}
- 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-rust.yml
b/.github/workflows/release-rust.yml
index 7585019..7a74ef6 100644
--- a/.github/workflows/release-rust.yml
+++ b/.github/workflows/release-rust.yml
@@ -17,7 +17,7 @@
# Publish paimon-mosaic-core crate to crates.io.
#
-# Trigger: push a version tag (e.g. v0.1.0, v0.1.0-rc1).
+# 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.
@@ -25,12 +25,13 @@
name: Release Rust
on:
- push:
- tags:
- - "v[0-9]+.[0-9]+.[0-9]+"
- - "v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+"
+ workflow_call:
workflow_dispatch:
+concurrency:
+ group: release-rust-${{ github.ref }}
+ cancel-in-progress: false
+
jobs:
publish:
runs-on: ubuntu-latest
@@ -48,7 +49,7 @@ jobs:
run: cargo publish -p paimon-mosaic-core --dry-run
- name: Publish paimon-mosaic-core to crates.io
- if: startsWith(github.ref, 'refs/tags/') && !contains(github.ref, '-')
+ if: github.repository == 'apache/paimon-mosaic' &&
startsWith(github.ref, 'refs/tags/') && !contains(github.ref_name, '-')
run: cargo publish -p paimon-mosaic-core
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
diff --git a/.github/workflows/release-rust.yml b/.github/workflows/release.yml
similarity index 51%
copy from .github/workflows/release-rust.yml
copy to .github/workflows/release.yml
index 7585019..7c1f516 100644
--- a/.github/workflows/release-rust.yml
+++ b/.github/workflows/release.yml
@@ -15,14 +15,10 @@
# specific language governing permissions and limitations
# under the License.
-# Publish paimon-mosaic-core crate to crates.io.
-#
-# Trigger: push a version tag (e.g. v0.1.0, v0.1.0-rc1).
-# Pre-release tags (containing '-') only run dry-run checks without publishing.
-#
-# Token auth: add secret CARGO_REGISTRY_TOKEN for crates.io publishing.
+# Orchestrate the full release while keeping each language release workflow
+# independently runnable through workflow_dispatch.
-name: Release Rust
+name: Release
on:
push:
@@ -31,24 +27,33 @@ on:
- "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:
- 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-mosaic-core --dry-run
-
- - name: Publish paimon-mosaic-core to crates.io
- if: startsWith(github.ref, 'refs/tags/') && !contains(github.ref, '-')
- run: cargo publish -p paimon-mosaic-core
- env:
- CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
+ 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: [rust, java, python-wheels]
+ if: startsWith(github.ref, 'refs/tags/')
+ uses: ./.github/workflows/release-python-publish.yml
+ secrets: inherit
diff --git a/docs/creating-a-release.html b/docs/creating-a-release.html
index 7895d08..8b69a4d 100644
--- a/docs/creating-a-release.html
+++ b/docs/creating-a-release.html
@@ -69,15 +69,15 @@
</ol>
<h3>Automated Publishing</h3>
- <p>When a version tag is pushed, GitHub Actions automatically
publishes language-specific artifacts:</p>
+ <p>When a version tag is pushed, the <code>Release</code> workflow
orchestrates language-specific release jobs:</p>
<table>
<thead>
- <tr><th>Component</th><th>Tag Pattern</th><th>Published
To</th><th>Pre-release (<code>-rc</code>) Behavior</th></tr>
+ <tr><th>Component</th><th>Tag Pattern</th><th>Published
To</th><th>Pre-release (<code>-rc</code>) Behavior</th><th>Ordering</th></tr>
</thead>
<tbody>
- <tr><td>Rust
crate</td><td><code>v0.1.0</code></td><td>crates.io</td><td>Dry-run
only</td></tr>
- <tr><td>Java
binding</td><td><code>v0.1.0</code></td><td>Apache Nexus
staging</td><td>Deploys to staging</td></tr>
- <tr><td>Python
binding</td><td><code>v0.1.0</code></td><td>PyPI</td><td>Publishes to
TestPyPI</td></tr>
+ <tr><td>Rust
crate</td><td><code>v0.1.0</code></td><td>crates.io</td><td>Dry-run
only</td><td>Runs in parallel</td></tr>
+ <tr><td>Java
binding</td><td><code>v0.1.0</code></td><td>Apache Nexus
staging</td><td>Deploys to staging</td><td>Runs in parallel</td></tr>
+ <tr><td>Python
binding</td><td><code>v0.1.0</code></td><td>PyPI</td><td>Publishes to
TestPyPI</td><td>Publishes only after Rust and Java jobs succeed</td></tr>
</tbody>
</table>
<p>The Release Manager's primary responsibility is managing the
<strong>source release</strong> (tarball + signature) and coordinating the
community vote. Language artifact publishing is handled by CI once the tag is
pushed.</p>
@@ -215,12 +215,14 @@ cd ..</code></pre>
git tag -v ${RC_TAG}
git push origin ${RELEASE_BRANCH}
git push origin ${RC_TAG}</code></pre>
- <p>After pushing, verify in <a
href="https://github.com/apache/paimon-mosaic/actions">GitHub Actions</a> that
all release workflows succeed:</p>
+ <p>After pushing, verify in <a
href="https://github.com/apache/paimon-mosaic/actions">GitHub Actions</a> that
the <code>Release</code> workflow succeeds:</p>
<ul>
- <li><strong>Release Rust</strong> — dry-run check (does
not publish for RC tags)</li>
- <li><strong>Release Java</strong> — builds native JNI
libraries for 4 platforms, deploys JAR to Apache Nexus staging</li>
- <li><strong>Release Python</strong> — builds wheels for
4 platforms, publishes to TestPyPI</li>
+ <li><strong>Rust release</strong> — dry-run check (does
not publish for RC tags)</li>
+ <li><strong>Java release</strong> — builds native JNI
libraries for 4 platforms, deploys JAR to Apache Nexus staging</li>
+ <li><strong>Python wheels</strong> — builds wheels for 4
platforms</li>
+ <li><strong>Python publish</strong> — publishes to
TestPyPI only after the Rust, Java, and Python wheel jobs succeed</li>
</ul>
+ <p>If only the Python publish job fails due to a transient PyPI or
TestPyPI issue, use <strong>Re-run failed jobs</strong> on the
<code>Release</code> workflow. The successful Rust, Java, and Python wheel jobs
do not need to be repeated.</p>
<h3>Create Source Release Artifacts</h3>
<p>Create source release artifacts from the same commit as the RC
tag:</p>
@@ -245,7 +247,7 @@ svn commit -m "Add paimon-mosaic ${RELEASE_VERSION}
RC${RC_NUM}"</code></pre>
<p><strong>Checklist:</strong></p>
<ul>
- <li>RC tag pushed and CI workflows succeeded</li>
+ <li>RC tag pushed and the CI release workflow succeeded</li>
<li>Source tarball, signature, and checksum staged to <a
href="https://dist.apache.org/repos/dist/dev/paimon/">dist.apache.org
dev</a></li>
<li>Java artifacts deployed to Nexus staging repository</li>
<li>Python wheels published to TestPyPI</li>
@@ -308,7 +310,7 @@ svn commit -m "Remove paimon-mosaic ${RELEASE_VERSION}
RC${RC_NUM} (superseded)"
<h2 id="finalize-the-release">Finalize the Release</h2>
<h3>Push the Release Tag</h3>
- <p>Once the vote passes, create and push the final release tag.
This triggers CI to publish to crates.io and PyPI automatically.</p>
+ <p>Once the vote passes, create and push the final release tag.
This triggers CI to publish to crates.io and PyPI automatically; PyPI
publishing runs only after the Rust and Java release jobs succeed.</p>
<pre><code>git checkout ${RC_TAG}
git tag -s ${RELEASE_TAG} -m "Release Apache Paimon Mosaic ${RELEASE_VERSION}"
git tag -v ${RELEASE_TAG}
diff --git a/docs/verifying-a-release-candidate.html
b/docs/verifying-a-release-candidate.html
index 1ec53e0..6f620de 100644
--- a/docs/verifying-a-release-candidate.html
+++ b/docs/verifying-a-release-candidate.html
@@ -170,7 +170,7 @@ paimon-mosaic-core = { git =
"https://github.com/apache/paimon-mosaic", tag = "v
</ol>
<h3>Python (TestPyPI)</h3>
- <p>The RC tag publishes wheels to TestPyPI. Install and verify:</p>
+ <p>The RC tag publishes wheels to TestPyPI after the Rust and Java
release jobs succeed. Install and verify:</p>
<pre><code>pip install -i https://test.pypi.org/simple/
paimon-mosaic==${RELEASE_VERSION}rc${RC_NUM}
python -c "import mosaic; print('OK')"</code></pre>
<p>Verify wheels are available for: Linux x86_64, Linux aarch64,
macOS aarch64, Windows x86_64.</p>