Create a release
This document mainly introduces how the release manager releases a new version of Apache OpenDAL™ in accordance with the Apache requirements.
Introduction
Source Release is the key point which Apache values, and is also necessary for an ASF release.
Please remember that publishing software has legal consequences.
This guide complements the foundation-wide policies and guides:
Release terminology
The release process uses the following terms:
release_version: the final version proposed for release, like0.46.0.release_candidate_version: the exact candidate used during staging and voting, formed from${release_version}-${rc_version}, like0.46.0-rc.1.opendal_last_version: the most recent final version, used as the starting point for the release comparison.rc_version: the suffix that identifies the release candidate and voting round, likerc.1.maven_artifact_number: the number of the Maven staging repository, like1010. Find it by searching for "opendal" in the Apache Nexus staging repositories. GitHub Actions creates the staged Maven artifacts when the release candidate tag is pushed.
Preparation
This section is the requirements for individuals who are new to the role of release manager.
Refer to Setup GPG Key to make sure the GPG key has been set up.
Start discussion about the next release
Start a discussion at OpenDAL Discussion General:
Title:
[DISCUSS] Release Apache OpenDAL ${release_version}
Content:
Hello, Apache OpenDAL community,
I would like to start a discussion about releasing Apache OpenDAL ${release_version}.
Changes since Apache OpenDAL ${opendal_last_version}:
https://github.com/apache/opendal/compare/v${opendal_last_version}...main
Please share any comments on this release plan. After the discussion, we will
update the repository versions and begin preparing the release candidate.
Thanks,
${name}
Start a tracking issue about the next release
Start a tracking issue on GitHub for the upcoming release to track all tasks that need to be completed.
Release List
Update the version list in the dev/src/release/package.rs file.
This file is the source of truth for the split source release layout. Each entry in this list produces an independent source archive named apache-opendal-{package}-{version}-src.tar.gz.
For example:
- If there is any breaking change, please bump the
minorversion instead of thepatchversion. - If this package is not ready for release, please skip it from the release list.
- Packages that have moved to separate repositories must not be included here.
GitHub Side
Bump version in project
Run just update-version to bump the version in the project.
Update docs
- Update
CHANGELOG.md, refer to Generate Release Note for more information. - Update
core/core/src/docs/upgrade.mdif there are breaking changes incore - Make sure every released bindings'
upgrade.mdhas been updated.- java:
bindings/java/upgrade.md - node.js:
bindings/nodejs/upgrade.md - python:
bindings/python/upgrade.md
- java:
Generate dependencies list
Download and setup cargo-deny. You can refer to cargo-deny.
Running python3 ./scripts/dependencies.py generate to update the dependency list of every package.
Bootstrap Rust crate names
During release preparation, wait until the intended Rust crate-name set is on
apache/opendal main. The release manager chooses the exact reservation time,
normally about three days before the planned release. At that time, check out
the current main commit and run:
.agents/skills/opendal-release/scripts/bootstrap-rust-crates.sh
The helper dispatches the manual Bootstrap Rust Crates workflow without
inputs. The workflow scans the Rust publish plan from the main commit that
GitHub records as the run's headSha. The helper resolves that exact run,
checks the headSha, and waits for it to finish. A PMC member must approve the
protected rust-bootstrap environment on every run.
The protected job authenticates ownership and audits every existing planned
crate before it writes anything. Each established crate must already have
apache/opendal, release_rust.yml, and the release environment as its only
Trusted Publisher, with trustpub_only enabled. The workflow never modifies an
established crate. For each missing name, it publishes a dependency-free
0.0.0 package, configures that Trusted Publisher, and enables
trustpub_only. Reruns reconcile every placeholder. The protected authenticated
audit runs even when there are no bootstrap candidates.
The one-time migration of existing crates is a separate administrative task.
Configure the same apache/opendal, release_rust.yml, and release publisher
identity and enable trustpub_only for every existing crate. This workflow
audits but never modifies established crates, and it fails if the migration is
incomplete. Complete that migration before using the OIDC-only Rust release
workflow.
The 0.0.0 package is a namespace reservation, not an ASF software release or
release artifact. crates.io exposes the reservation publicly, and publishing it
is irreversible. The release manager decides when the names are stable enough
to reserve. Run the helper again if a later main commit adds another planned
crate. Do not create the RC tag until every name in the current publish plan has
passed the workflow.
ASF Infrastructure provisions the rust-bootstrap GitHub environment from
.asf.yaml with PMC required reviewers, self-review disabled, and a main-only
deployment policy. A PMC release manager must add a
CARGO_REGISTRY_BOOTSTRAP_TOKEN environment secret. The crates.io token must
have only the publish-new and trusted-publishing endpoint scopes and
OpenDAL-specific crate scopes. The normal Rust release workflow must not have
access to this token.
Push release candidate tag
After bump version PR gets merged, push the release candidate tag:
- Create a tag at
mainbranch on theBump Version/Patch up versioncommit:git tag -s "v0.46.0-rc.1", please correctly check out the corresponding commit instead of directly tagging on the main branch. - Push tags to GitHub:
git push --tags.
Pushing an RC tag to GitHub will trigger the tag-based release workflows. In the current flow, this includes the Java staging artifacts on https://repository.apache.org and dry-run checks for other released packages.
Check the GitHub action status
After pushing the tag, check the GitHub action status to make sure the RC workflows finished successfully.
- Rust packages: Release Rust Packages
- Python: Release Python Binding
- Java: Bindings Java CI and Bindings Java Release
- Node.js: Bindings Node.js CI and Release NodeJS Binding
- Docs: Docs
In the most cases, it would be great to rerun the failed workflow directly when you find some failures. But if a new code patch is needed to fix the failure, you should create a new release candidate tag, increase the rc number and push it to GitHub.
Check Rust crates.io readiness
Before the official release tag is pushed, check the Rust package publish plan:
python3 .github/scripts/release_rust/plan.py
The plan must include every Rust package that is referenced by released crates.
For example, opendal-testkit is referenced by the opendal crate's tests
feature, so it must be publishable and appear before opendal in the plan.
The Rust release workflow uses .github/scripts/release_rust/publish.py instead
of calling cargo publish directly. The helper temporarily removes repo-local
dev-dependencies while packaging crates, because crates.io resolves
dev-dependencies even when cargo publish --no-verify is used. Without this
step, same-version dev dependencies and dev-only cycles can block the release
even though they are not needed by downstream users.
The normal workflow publishes only through Trusted Publishing. The helper fetches and revokes a fresh OIDC-derived crates.io token for every publish attempt, including attempts retried after a rate limit. New crate names must already exist from the reservation workflow.
ASF Side
If any step in the ASF Release process fails and requires code changes, we will abandon that version and prepare for the next one. Our release page will only display ASF releases instead of GitHub Releases.
Additionally, we should also drop the staging Maven artifacts on https://repository.apache.org.
Create an ASF Release
After the RC tag has been pushed and the required workflows are green, create the ASF source release artifacts.
- Checkout to released tag. (e.g.
git checkout v0.46.0-rc.1, tag is created in the previous step) - Use the release script to create a new release:
just release- This script will generate the release candidate artifacts under
distfor every package listed indev/src/release/package.rs, including:apache-opendal-{package}-{version}-src.tar.gzapache-opendal-{package}-{version}-src.tar.gz.ascapache-opendal-{package}-{version}-src.tar.gz.sha512
- Artifact names use each package's own version. The RC version is only used for the SVN directory name, such as
0.55.0-rc.1/. - Each archive contains
LICENSE,NOTICE, the package directory itself, and any repo-local dependencies needed to build that package from source. - This repository no longer produces a monolithic
apache-opendal-${release_version}-src.tar.gzartifact or anyapache-opendal-bin-*artifacts.
- This script will generate the release candidate artifacts under
This script will create a new release under dist.
For example:
dist
├── apache-opendal-bindings-c-${c_version}-src.tar.gz
├── apache-opendal-bindings-c-${c_version}-src.tar.gz.asc
├── apache-opendal-bindings-c-${c_version}-src.tar.gz.sha512
├── apache-opendal-bindings-cpp-${cpp_version}-src.tar.gz
├── apache-opendal-bindings-cpp-${cpp_version}-src.tar.gz.asc
├── apache-opendal-bindings-cpp-${cpp_version}-src.tar.gz.sha512
├── apache-opendal-bindings-java-${java_version}-src.tar.gz
├── apache-opendal-bindings-java-${java_version}-src.tar.gz.asc
├── apache-opendal-bindings-java-${java_version}-src.tar.gz.sha512
├── apache-opendal-bindings-nodejs-${nodejs_version}-src.tar.gz
├── apache-opendal-bindings-nodejs-${nodejs_version}-src.tar.gz.asc
├── apache-opendal-bindings-nodejs-${nodejs_version}-src.tar.gz.sha512
├── apache-opendal-bindings-python-${python_version}-src.tar.gz
├── apache-opendal-bindings-python-${python_version}-src.tar.gz.asc
├── apache-opendal-bindings-python-${python_version}-src.tar.gz.sha512
├── apache-opendal-core-${core_version}-src.tar.gz
├── apache-opendal-core-${core_version}-src.tar.gz.asc
├── apache-opendal-core-${core_version}-src.tar.gz.sha512
├── apache-opendal-integrations-dav-server-${dav_server_version}-src.tar.gz
├── apache-opendal-integrations-dav-server-${dav_server_version}-src.tar.gz.asc
├── apache-opendal-integrations-dav-server-${dav_server_version}-src.tar.gz.sha512
├── apache-opendal-integrations-object_store-${object_store_version}-src.tar.gz
├── apache-opendal-integrations-object_store-${object_store_version}-src.tar.gz.asc
├── apache-opendal-integrations-object_store-${object_store_version}-src.tar.gz.sha512
├── apache-opendal-integrations-parquet-${parquet_version}-src.tar.gz
├── apache-opendal-integrations-parquet-${parquet_version}-src.tar.gz.asc
├── apache-opendal-integrations-parquet-${parquet_version}-src.tar.gz.sha512
├── apache-opendal-integrations-unftp-sbe-${unftp_sbe_version}-src.tar.gz
├── apache-opendal-integrations-unftp-sbe-${unftp_sbe_version}-src.tar.gz.asc
└── apache-opendal-integrations-unftp-sbe-${unftp_sbe_version}-src.tar.gz.sha512
Upload artifacts to the SVN dist repo
SVN is required for this step.
The svn repository of the dev branch is: https://dist.apache.org/repos/dist/dev/opendal
First, checkout OpenDAL to local directory:
# As this step will copy all the versions, it will take some time. If the network is broken, please use svn cleanup to delete the lock before re-execute it.
svn co https://dist.apache.org/repos/dist/dev/opendal opendal-dist-dev
Then, upload the artifacts:
The
${release_candidate_version}here should be like0.46.0-rc.1.
cd opendal-dist-dev
# create a directory named by version
mkdir ${release_candidate_version}
# copy source code and signature package to the versioned directory
cp ${repo_dir}/dist/* ${release_candidate_version}/
# check svn status
svn status
# add to svn
svn add ${release_candidate_version}
# check svn status
svn status
# commit to SVN remote server
svn commit -m "Prepare for ${release_candidate_version}"
Visit https://dist.apache.org/repos/dist/dev/opendal/ to make sure the artifacts are uploaded correctly.
Close the Nexus staging repo
To verify the Maven staging artifacts in the next step, close the Nexus staging repo as below.
- Open https://repository.apache.org/#stagingRepositories with your Apache ID login.
- Find the artifact
orgapacheopendal-${maven_artifact_number}, click the "Close" button.
The close operation means that the artifacts are ready for voting.
If the vote failed, click "Drop" to drop the staging Maven artifacts.
Rescue
If you publish incorrect or unexpected artifacts, such as invalid signatures or
checksums, abandon the current release candidate ${release_candidate_version},
increment rc_version, and prepare a new candidate. Delete the incorrect
artifacts from the SVN dist repository and drop the Maven staging repository at
https://repository.apache.org.
Voting
A release candidate must receive at least three binding +1 votes from OpenDAL
PMC members and more binding +1 than -1 votes.
Start a VOTE at OpenDAL Discussion General:
Title:
[VOTE] Release Apache OpenDAL ${release_candidate_version}
Content:
Hello, Apache OpenDAL community,
This is a call for a vote on Apache OpenDAL release candidate
v${release_candidate_version}, proposed for release as Apache OpenDAL ${release_version}.
Release candidate source packages:
https://dist.apache.org/repos/dist/dev/opendal/${release_candidate_version}/
Keys used to verify the signatures:
https://downloads.apache.org/opendal/KEYS
Release candidate Git tag:
https://github.com/apache/opendal/releases/tag/v${release_candidate_version}
Maven staging repository:
https://repository.apache.org/content/repositories/orgapacheopendal-${maven_artifact_number}/
Python packages on TestPyPI:
https://test.pypi.org/project/opendal/
Staged website:
https://opendal-v${release_candidate_version | replace('.', '-')}.staged.apache.org/
Please download, verify, and test the release candidate. When voting, state
which checks you performed.
The vote will remain open for at least 72 hours and until the release candidate
receives at least three binding `+1` votes, with more binding `+1` than `-1`
votes.
```markdown
- [ ] +1 Approve.
- [ ] +0 No opinion.
- [ ] -1 Do not approve. Please explain why.
Verification checklist:
- [ ] Download links work.
- [ ] Checksums and signatures are valid.
- [ ] LICENSE and NOTICE files are present.
- [ ] Source packages contain no unexpected binary files.
- [ ] Source files include ASF license headers.
- [ ] Source builds successfully.
```
Use `verify.py` to help verify the release candidate:
```shell
svn checkout https://dist.apache.org/repos/dist/dev/opendal/${release_candidate_version}/ opendal-dist-${release_candidate_version}
cd opendal-dist-${release_candidate_version}
curl --silent --show-error --location https://github.com/apache/opendal/raw/v${release_candidate_version}/scripts/verify.py --output verify.py
python verify.py
```
For more information about Apache OpenDAL, visit https://opendal.apache.org/.
Thanks,
${name}
Example: https://github.com/apache/opendal/discussions/5211
The vote should remain open for at least 72 hours, except in the following cases:
- Security issues
- Urgent bug fixes that affect many users
- Other emergencies approved by the PMC
The release manager must explain the emergency and shortened voting period in the vote message.
The 72-hour minimum gives community members in different time zones an opportunity to verify the release candidate and vote.
After the release candidate receives at least three binding +1 votes from OpenDAL PMC members, with more binding +1 than -1 votes, announce the result:
Title:
[RESULT][VOTE] Release Apache OpenDAL ${release_candidate_version}
Content:
Hello, Apache OpenDAL community,
The vote on Apache OpenDAL release candidate v${release_candidate_version} has passed.
The candidate is approved for release as Apache OpenDAL ${release_version}.
Binding +1 votes:
- xxx
- yyy
- zzz
Non-binding +1 votes:
- aaa
+0 votes:
- None
-1 votes:
- None
Vote thread: ${vote_thread_url}
Thanks,
${name}
Identify voters by their real name, public profile name, or Apache ID. Avoid nicknames that make votes difficult to verify. Confirm that every binding vote comes from an OpenDAL PMC member.
Example: https://lists.apache.org/thread/xk5myl10mztcfotn59oo59s4ckvojds6
Official Release
Push the release git tag
# Checkout the tags that passed VOTE
git checkout v${release_candidate_version}
# Tag with the final release version
git tag -s v${release_version}
# Push tags to GitHub to trigger releases
git push origin v${release_version}
Publish artifacts to SVN RELEASE branch
svn mv https://dist.apache.org/repos/dist/dev/opendal/${release_candidate_version} https://dist.apache.org/repos/dist/release/opendal/${release_version} -m "Release ${release_version}"
Release Maven artifacts
- Open https://repository.apache.org/#stagingRepositories.
- Find the artifact
orgapacheopendal-${maven_artifact_number}, click the "Release" button.
It will take some time to sync the Maven artifacts to the Maven Central.
If the vote failed, click "Drop" to drop the staging Maven artifacts.
Check the language binding artifacts
We need to check the language binding artifacts in the language package repo to make sure they are released successfully.
- Rust: https://crates.io/crates/opendal
- Python: https://pypi.org/project/opendal/
- Java: https://repository.apache.org/#nexus-search;quick~opendal
- Node.js: https://www.npmjs.com/package/opendal
For Rust crates, check both the top-level opendal crate and split crates that
were added or changed in the release, such as opendal-service-*,
opendal-layer-*, and integration crates.
For Java binding, if we cannot find the latest version of artifacts in the repo,
we need to check the orgapacheopendal-${maven_artifact_number} artifact status in staging repo.
For non-Java bindings, if we cannot find the latest version of artifacts in the repo, we need to check the GitHub action status.
Create a GitHub Release
- Click here to create a new release.
- Pick the git tag of this release version from the dropdown menu.
- Make sure the branch target is
main. - Generate the release note by clicking the
Generate release notesbutton. - Add the release note from every component's
upgrade.mdif there are breaking changes before the content generated by GitHub. Check them carefully. - Publish the release.
Send the announcement
Post an announcement in OpenDAL Discussion Announcements and send the same content to announce@apache.org.
Follow the Committer Email guide to configure SMTP before sending to the announcement mailing list.
Summarize notable user-facing changes instead of copying the raw breaking-change list.
Title:
[ANNOUNCE] Release Apache OpenDAL ${release_version}
Content:
Hi all,
The Apache OpenDAL community is pleased to announce the release of
Apache OpenDAL ${release_version}.
Apache OpenDAL provides a unified data access layer for applications,
libraries, and data systems.
Notable changes in this release include:
1. xxxxx
2. yyyyyy
3. zzzzzz
Changelog: https://github.com/apache/opendal/releases/tag/v${release_version}
Download: https://opendal.apache.org/download
Website: https://opendal.apache.org/
Issue tracker: https://github.com/apache/opendal/issues
Mailing list: dev@opendal.apache.org
Thanks,
${name}
on behalf of the Apache OpenDAL community
Example: https://lists.apache.org/thread/oy77n55brvk72tnlb2bjzfs9nz3cfd0s
Post release
After the official release out, you may perform a few post-actions.
Remove the old releases
Remove the old releases if any. You only need the latest release there, and older releases are available through the Apache archive.
To clean up old releases, run:
# 1. Get the list of releases
svn ls https://dist.apache.org/repos/dist/release/opendal
# 2. Delete each release (except for the last one)
svn del -m "Archiving OpenDAL release X.Y.Z" https://dist.apache.org/repos/dist/release/opendal/X.Y.Z