Creating a Release

Guide for Release Managers of Apache Paimon Mosaic, following ASF Release Policy.

Overview

The release process consists of:

  1. Decide to release
  2. Prepare for the release
  3. Build a release candidate
  4. Vote on the release candidate
  5. If necessary, fix any issues and go back to step 3
  6. Finalize the release
  7. Promote the release

Automated Publishing

When a version tag is pushed, the Release workflow orchestrates language-specific release jobs:

ComponentTag PatternPublished ToPre-release (-rc) BehaviorOrdering
Rust cratev0.1.0crates.ioDry-run onlyRuns in parallel
Java bindingv0.1.0Apache Nexus stagingDeploys to stagingRuns in parallel
Python bindingv0.1.0PyPIPublishes to TestPyPIPublishes only after Rust and Java jobs succeed

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 tag is pushed.

Decide to Release

Anybody can propose a release on the dev mailing list, giving a short rationale and nominating a committer as Release Manager (including themselves).

Checklist:

Prepare for the Release

One-time Release Manager Setup

GPG Key

  1. Install GnuPG:

    # macOS
    brew install gnupg
    
    # Ubuntu / Debian
    sudo apt install gnupg2
  2. Generate a key pair (RSA 4096, does not expire, use your @apache.org email):

    gpg --full-gen-key
  3. Find your key ID:

    gpg --list-keys --keyid-format short
  4. Upload to key server:

    gpg --keyserver hkps://keyserver.ubuntu.com --send-keys <YOUR_KEY_ID>
  5. Append to the project KEYS file:

    svn co https://dist.apache.org/repos/dist/release/paimon/ paimon-dist-release --depth=files
    cd paimon-dist-release
    (gpg --list-sigs <YOUR_KEY_ID> && gpg --armor --export <YOUR_KEY_ID>) >> KEYS
    svn ci -m "Add <YOUR_NAME>'s public key"
  6. Configure Git to sign tags:

    git config --global user.signingkey <YOUR_KEY_ID>

GitHub Actions Secrets

Ensure the following repository secrets are configured:

SecretPurpose
CARGO_REGISTRY_TOKENcrates.io publishing
NEXUS_STAGE_DEPLOYER_USERApache Nexus staging
NEXUS_STAGE_DEPLOYER_PWApache Nexus staging
GPG_SECRET_KEYJava artifact signing
GPG_PASSPHRASEJava artifact signing
PYPI_API_TOKENPyPI publishing
TEST_PYPI_API_TOKENTestPyPI publishing

Clone into a Fresh Workspace

git clone https://github.com/apache/paimon-mosaic.git
cd paimon-mosaic

Set Up Environment Variables

RELEASE_VERSION="0.1.0"
SHORT_RELEASE_VERSION="0.1"
NEXT_VERSION="0.2.0"
RC_NUM="1"
RELEASE_BRANCH="release-${SHORT_RELEASE_VERSION}"
RC_TAG="v${RELEASE_VERSION}-rc${RC_NUM}"
RELEASE_TAG="v${RELEASE_VERSION}"

Generate Dependency License Manifests

ASF releases must include a declaration of third-party dependency licenses. Generate and commit dependency manifests on main before creating the release branch, so the release branch and source archive contain the same reviewed manifests.

# Install cargo-deny (one-time)
cargo install cargo-deny

git checkout main
git pull --ff-only origin main

# Check all dependency licenses are approved
python3 tools/dependencies.py check

# Generate DEPENDENCIES.rust.tsv
python3 tools/dependencies.py generate

git add DEPENDENCIES.rust.tsv core/DEPENDENCIES.rust.tsv ffi/DEPENDENCIES.rust.tsv jni/DEPENDENCIES.rust.tsv
git commit -m "chore: update dependency list for release ${RELEASE_VERSION}"
git push origin main

Fix any license violations before proceeding. The generated files must be committed before tagging because create_source_release.sh builds the archive from the committed Git tree.

Create a Release Branch

Create one stable release branch for the release line. Do not include the RC number in the branch name; RC attempts are represented by tags such as v0.1.0-rc1 and v0.1.0-rc2.

git checkout main
git pull --ff-only origin main
git checkout -b ${RELEASE_BRANCH}
git push origin ${RELEASE_BRANCH}

Bump Version on Main

After cutting the release branch, bump main to the next development version:

git checkout main
git pull --ff-only origin main
cd tools
OLD_VERSION=${RELEASE_VERSION} NEW_VERSION=${NEXT_VERSION}-SNAPSHOT ./update_branch_version.sh
cd ..
git push origin main

The script updates version strings in all pom.xml (matching both ${OLD_VERSION} and ${OLD_VERSION}-SNAPSHOT), Cargo.toml (excluding target/), and python/pyproject.toml files, then creates a commit.

Build a Release Candidate

Update Versions on the Release Branch

Before tagging, update the release branch to non-SNAPSHOT versions (removing -SNAPSHOT from pom.xml):

git checkout ${RELEASE_BRANCH}
git pull --ff-only origin ${RELEASE_BRANCH}
cd tools
OLD_VERSION=${RELEASE_VERSION}-SNAPSHOT NEW_VERSION=${RELEASE_VERSION} ./update_branch_version.sh
cd ..

This ensures the Java artifacts deployed from the RC tag carry the final release version (e.g. 0.1.0) rather than a SNAPSHOT suffix.

Create the RC Tag

Push a signed RC tag to trigger CI workflows:

git tag -s ${RC_TAG} -m "${RC_TAG}"
git tag -v ${RC_TAG}
git push origin ${RELEASE_BRANCH}
git push origin ${RC_TAG}

After pushing, verify in GitHub Actions that the Release workflow succeeds:

If only the Python publish job fails due to a transient PyPI or TestPyPI issue, use Re-run failed jobs on the Release workflow. The successful Rust, Java, and Python wheel jobs do not need to be repeated.

Create Source Release Artifacts

Create source release artifacts from the same commit as the RC tag:

git checkout ${RC_TAG}
cd tools
RELEASE_VERSION=${RELEASE_VERSION} ./create_source_release.sh

This creates the following under tools/release/:

Stage Artifacts to SVN

svn checkout https://dist.apache.org/repos/dist/dev/paimon/ paimon-dist-dev --depth=immediates
cd paimon-dist-dev
mkdir paimon-mosaic-${RELEASE_VERSION}-rc${RC_NUM}
cp ../paimon-mosaic/tools/release/apache-paimon-mosaic-${RELEASE_VERSION}-src.* \
   paimon-mosaic-${RELEASE_VERSION}-rc${RC_NUM}/
svn add paimon-mosaic-${RELEASE_VERSION}-rc${RC_NUM}
svn commit -m "Add paimon-mosaic ${RELEASE_VERSION} RC${RC_NUM}"

Checklist:

Vote on the Release Candidate

Start a vote on the dev mailing list.

Subject: [VOTE] Release Apache Paimon Mosaic ${RELEASE_VERSION} (RC${RC_NUM})

Hi everyone,

Please review and vote on release candidate #${RC_NUM} for Apache Paimon Mosaic ${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-mosaic-${RELEASE_VERSION}-rc${RC_NUM}/

Git tag:
https://github.com/apache/paimon-mosaic/releases/tag/${RC_TAG}

Git commit:
<RC_COMMIT_SHA>

KEYS for signature verification:
https://downloads.apache.org/paimon/KEYS

Java artifacts (Nexus staging):
https://repository.apache.org/content/repositories/orgapachepaimon-XXXX/

Python wheels (TestPyPI):
https://test.pypi.org/project/paimon-mosaic/${RELEASE_VERSION}rc${RC_NUM}/

Verification guide:
https://github.com/apache/paimon-mosaic/blob/main/docs/verifying-a-release-candidate.html

The vote will be open for at least 72 hours.

Thanks,
Release Manager

After the vote passes, send a result email with subject: [RESULT][VOTE] Release Apache Paimon Mosaic ${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):
    cd paimon-dist-dev
    svn remove paimon-mosaic-${RELEASE_VERSION}-rc${RC_NUM}
    svn commit -m "Remove paimon-mosaic ${RELEASE_VERSION} RC${RC_NUM} (superseded)"
  3. Drop the Nexus staging repository via the Nexus UI.
  4. Increment RC_NUM, then go back to 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 and PyPI automatically; PyPI publishing runs only after the Rust and Java release jobs succeed.

git checkout ${RC_TAG}
git tag -s ${RELEASE_TAG} -m "Release Apache Paimon Mosaic ${RELEASE_VERSION}"
git tag -v ${RELEASE_TAG}
git push origin ${RELEASE_TAG}

Move Source Artifacts to the Release Repository

svn mv -m "Release paimon-mosaic ${RELEASE_VERSION}" \
  https://dist.apache.org/repos/dist/dev/paimon/paimon-mosaic-${RELEASE_VERSION}-rc${RC_NUM} \
  https://dist.apache.org/repos/dist/release/paimon/paimon-mosaic-${RELEASE_VERSION}

Release the Java Artifacts

  1. Go to Apache Nexus Staging Repositories.
  2. Find the staging repository for orgapachepaimon-XXXX.
  3. Close the repository (runs validation rules).
  4. Once closed, Release the repository to Maven Central.

Verify Published Artifacts

Create GitHub Release

  1. Go to Releases — New release.
  2. Choose tag v${RELEASE_VERSION}.
  3. Click Generate release notes and review.
  4. Click Publish release.

Checklist:

Promote the Release

Update the Releases Page

Update the Releases 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 Mosaic ${RELEASE_VERSION}

The Apache Paimon community is pleased to announce the release of
Apache Paimon Mosaic ${RELEASE_VERSION}.

Apache Paimon Mosaic is a columnar-bucket hybrid format optimized for
wide tables of Apache Paimon.

Rust:   cargo add paimon-mosaic-core
Java:   <dependency>
          <groupId>org.apache.paimon</groupId>
          <artifactId>mosaic</artifactId>
          <version>${RELEASE_VERSION}</version>
        </dependency>
Python: pip install paimon-mosaic

Release notes:
https://github.com/apache/paimon-mosaic/releases/tag/v${RELEASE_VERSION}

Thanks to all contributors!

Clean Up Old Releases

ASF policy requires only the latest release in the dist release area. Remove older versions:

svn rm -m "Remove old release" \
  https://dist.apache.org/repos/dist/release/paimon/paimon-mosaic-${OLD_VERSION}