blob: 1a988492825ea0c9573ea66694c3521ad227e6de [file] [log] [blame]
"use strict";(self.webpackChunkwebsite_next=self.webpackChunkwebsite_next||[]).push([[40599],{15680:(e,a,t)=>{t.d(a,{xA:()=>c,yg:()=>d});var n=t(96540);function r(e,a,t){return a in e?Object.defineProperty(e,a,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[a]=t,e}function l(e,a){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);a&&(n=n.filter((function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable}))),t.push.apply(t,n)}return t}function s(e){for(var a=1;a<arguments.length;a++){var t=null!=arguments[a]?arguments[a]:{};a%2?l(Object(t),!0).forEach((function(a){r(e,a,t[a])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):l(Object(t)).forEach((function(a){Object.defineProperty(e,a,Object.getOwnPropertyDescriptor(t,a))}))}return e}function o(e,a){if(null==e)return{};var t,n,r=function(e,a){if(null==e)return{};var t,n,r={},l=Object.keys(e);for(n=0;n<l.length;n++)t=l[n],a.indexOf(t)>=0||(r[t]=e[t]);return r}(e,a);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(n=0;n<l.length;n++)t=l[n],a.indexOf(t)>=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var i=n.createContext({}),p=function(e){var a=n.useContext(i),t=a;return e&&(t="function"==typeof e?e(a):s(s({},a),e)),t},c=function(e){var a=p(e.components);return n.createElement(i.Provider,{value:a},e.children)},h="mdxType",u={inlineCode:"code",wrapper:function(e){var a=e.children;return n.createElement(n.Fragment,{},a)}},g=n.forwardRef((function(e,a){var t=e.components,r=e.mdxType,l=e.originalType,i=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),h=p(t),g=r,d=h["".concat(i,".").concat(g)]||h[g]||u[g]||l;return t?n.createElement(d,s(s({ref:a},c),{},{components:t})):n.createElement(d,s({ref:a},c))}));function d(e,a){var t=arguments,r=a&&a.mdxType;if("string"==typeof e||r){var l=t.length,s=new Array(l);s[0]=g;var o={};for(var i in a)hasOwnProperty.call(a,i)&&(o[i]=a[i]);o.originalType=e,o[h]="string"==typeof e?e:r,s[1]=o;for(var p=2;p<l;p++)s[p]=t[p];return n.createElement.apply(null,s)}return n.createElement.apply(null,t)}g.displayName="MDXCreateElement"},70711:(e,a,t)=>{t.r(a),t.d(a,{assets:()=>i,contentTitle:()=>s,default:()=>u,frontMatter:()=>l,metadata:()=>o,toc:()=>p});var n=t(58168),r=(t(96540),t(15680));const l={id:"release-process",title:"Release process"},s=void 0,o={unversionedId:"release-process",id:"release-process",title:"Release process",description:"This page contains instructions for Pulsar committers on how to perform a release.",source:"@site/contribute/release-process.md",sourceDirName:".",slug:"/release-process",permalink:"/contribute/release-process",draft:!1,editUrl:"https://github.com/apache/pulsar-site/edit/main/contribute/release-process.md",tags:[],version:"current",lastUpdatedBy:"github-actions[bot]",lastUpdatedAt:1713836078,formattedLastUpdatedAt:"Apr 23, 2024",frontMatter:{id:"release-process",title:"Release process"},sidebar:"sidebarDevelopment",previous:{title:"Release policy",permalink:"/contribute/release-policy"},next:{title:"Creating GPG keys",permalink:"/contribute/create-gpg-keys"}},i={},p=[{value:"Preparation",id:"preparation",level:2},{value:"Set environment variables to be used across the commands",id:"env-vars",level:2},{value:"Create a release candidate",id:"create-a-release-candidate",level:2},{value:"Create the release branch",id:"create-the-release-branch",level:3},{value:"Cherry-picking changes scheduled for the release",id:"cherry-picking-changes-scheduled-for-the-release",level:3},{value:"Update project version and tag",id:"update-project-version-and-tag",level:3},{value:"Build release artifacts",id:"build-release-artifacts",level:3},{value:"Check licenses",id:"check-licenses",level:3},{value:"Create and publish the GPG key if you haven&#39;t already done this",id:"create-and-publish-the-gpg-key-if-you-havent-already-done-this",level:3},{value:"Sign and stage the artifacts to local SVN directory",id:"sign-and-stage-the-artifacts-to-local-svn-directory",level:3},{value:"Validate the release files",id:"validate-the-release-files",level:3},{value:"Commit and upload the staged files in the local SVN directory to ASF SVN server",id:"commit-and-upload-the-staged-files-in-the-local-svn-directory-to-asf-svn-server",level:3},{value:"Stage Maven modules",id:"stage-maven-modules",level:3},{value:"Stage Docker images",id:"stage-docker-images",level:3},{value:"Release before Pulsar 3.0",id:"release-before-pulsar-30",level:4},{value:"Release Pulsar 3.0 and later",id:"release-pulsar-30-and-later",level:3},{value:"Call for the vote to release a version based on the release candidate",id:"call-for-the-vote-to-release-a-version-based-on-the-release-candidate",level:2},{value:"Summarize the voting for the release",id:"summarize-the-voting-for-the-release",level:2},{value:"Promote the release",id:"promote-the-release",level:2},{value:"Publish the final tag",id:"publish-the-final-tag",level:3},{value:"Create release notes in GitHub",id:"create-release-notes-in-github",level:3},{value:"Release the artifacts on SVN",id:"release-the-artifacts-on-svn",level:3},{value:"Release Maven modules",id:"release-maven-modules",level:3},{value:"Release Docker images",id:"release-docker-images",level:3},{value:"Release Helm Chart",id:"release-helm-chart",level:3},{value:"Release Homebrew libpulsar package",id:"release-homebrew-libpulsar-package",level:3},{value:"Release Python client",id:"release-python-client",level:3},{value:"Linux",id:"linux",level:4},{value:"macOS",id:"macos",level:4},{value:"Update the document",id:"update-the-document",level:2},{value:"Release notes",id:"release-notes",level:3},{value:"Swagger files",id:"swagger-files",level:3},{value:"Javadoc",id:"javadoc",level:3},{value:"Reference",id:"reference",level:3},{value:"Update <code>/docs</code> redirect",id:"update-docs-redirect",level:2},{value:"Update <code>/docs</code> version list dropdown",id:"update-docs-version-list-dropdown",level:2},{value:"Announce the release",id:"announce-the-release",level:2},{value:"Write a blog post (optional)",id:"write-a-blog-post-optional",level:2},{value:"Remove old releases",id:"remove-old-releases",level:2},{value:"Move to next version in pom.xml",id:"move-to-next-version-in-pomxml",level:2},{value:"Feature releases (master branch)",id:"feature-releases-master-branch",level:3},{value:"For maintenance branches",id:"for-maintenance-branches",level:3}],c={toc:p},h="wrapper";function u(e){let{components:a,...t}=e;return(0,r.yg)(h,(0,n.A)({},c,t,{components:a,mdxType:"MDXLayout"}),(0,r.yg)("p",null,"This page contains instructions for Pulsar committers on how to perform a release."),(0,r.yg)("p",null,"The term feature/patch releases used throughout this document is defined as follows:"),(0,r.yg)("ul",null,(0,r.yg)("li",{parentName:"ul"},"Feature releases contain 2.10.0, 2.11.0, 3.0.0, and so on."),(0,r.yg)("li",{parentName:"ul"},"Patch releases refer to bug-fix releases, such as 2.10.1, 2.10.2, and so on.")),(0,r.yg)("h2",{id:"preparation"},"Preparation"),(0,r.yg)("p",null,"Open a discussion on ",(0,r.yg)("a",{parentName:"p",href:"mailto:dev@pulsar.apache.org"},"dev@pulsar.apache.org")," to notify others that you volunteer to be the release manager of a specific release. If there are no disagreements, you can start the release process."),(0,r.yg)("p",null,"For feature releases, you should create a new branch named ",(0,r.yg)("inlineCode",{parentName:"p"},"branch-X.Y")," once all PRs with the X.Y.0 milestone are merged. If some PRs with the X.Y.0 milestone are still working in progress and might take much time to complete, you can move them to the next milestone if they are not important. In this case, you'd better notify the author in the PR."),(0,r.yg)("p",null,"For patch releases, if there are no disagreements, you should cherry-pick all merged PRs labeled with ",(0,r.yg)("inlineCode",{parentName:"p"},"release/X.Y.Z")," into ",(0,r.yg)("inlineCode",{parentName:"p"},"branch-X.Y"),". After these PRs are cherry-picked, you should add the ",(0,r.yg)("inlineCode",{parentName:"p"},"cherry-picked/branch-X.Y")," labels."),(0,r.yg)("p",null,"Sometimes some PRs cannot be cherry-picked cleanly, you might need to create a separate PR and move the ",(0,r.yg)("inlineCode",{parentName:"p"},"release/X.Y.Z")," label from the original PR to it. In this case, you can ask the author to help create the new PR."),(0,r.yg)("p",null,"For PRs that are still open, you can choose to delay them to the next release or ping others to review so that they can be merged."),(0,r.yg)("p",null,"To verify the release branch is not broken, you can synchronize the branch in your personal repo and open a PR to trigger the CI."),(0,r.yg)("p",null,"You can use the following command to catch basic compilation, checkstyle or spotbugs errors in your local env before cherry-picking."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-bash"},"mvn clean install -DskipTests\n")),(0,r.yg)("p",null,"If you haven't already done it, ",(0,r.yg)("a",{parentName:"p",href:"/contribute/create-gpg-keys"},"create and publish the GPG key"),". You will use the key to sign the release artifacts."),(0,r.yg)("p",null,"Before you start the next release steps, make sure you have installed these software:"),(0,r.yg)("ul",null,(0,r.yg)("li",{parentName:"ul"},"JDK 17 (for Pulsar version >= 2.11) or JDK 11 (for earlier versions)"),(0,r.yg)("li",{parentName:"ul"},"Maven 3.8.6"),(0,r.yg)("li",{parentName:"ul"},"Zip")),(0,r.yg)("p",null,"Also, you need to ",(0,r.yg)("strong",{parentName:"p"},"clean up the bookkeeper's local compiled")," to make sure the bookkeeper dependency is fetched from the Maven repository, details to see ",(0,r.yg)("a",{parentName:"p",href:"https://lists.apache.org/thread/gsbh95b2d9xtcg5fmtxpm9k9q6w68gd2"},"this mailing list thread"),"."),(0,r.yg)("h2",{id:"env-vars"},"Set environment variables to be used across the commands"),(0,r.yg)("p",null,"Set version"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"export VERSION_RC=3.0.4-candidate-1\nexport VERSION_WITHOUT_RC=${VERSION_RC%-candidate-*}\nexport VERSION_BRANCH=branch-3.0\nexport UPSTREAM_REMOTE=origin\n")),(0,r.yg)("p",null,"Set your ASF user id"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"export APACHE_USER=<your ASF userid>\n")),(0,r.yg)("p",null,"In addition, you will need to set ",(0,r.yg)("inlineCode",{parentName:"p"},"PULSAR_PATH")," to point to the cleanly checked out working directory for the release branch."),(0,r.yg)("p",null,"If you run into problems with GPG signing set this"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre"},"export GPG_TTY=$(tty)\n")),(0,r.yg)("p",null,"For some commands, a template is copied to the clipboard using ",(0,r.yg)("inlineCode",{parentName:"p"},"pbcopy"),".\nThis is already available on MacOS. For Linux, create a shell alias:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'# Linux only\n# install xsel if it is missing\nsudo apt install xsel\n# create alias pbcopy for copying stdin to clipboard\nalias pbcopy="xsel --clipboard --input"\n')),(0,r.yg)("h2",{id:"create-a-release-candidate"},"Create a release candidate"),(0,r.yg)("h3",{id:"create-the-release-branch"},"Create the release branch"),(0,r.yg)("p",null,"We are going to create a branch from ",(0,r.yg)("inlineCode",{parentName:"p"},"master")," to ",(0,r.yg)("inlineCode",{parentName:"p"},"branch-v2.X")," where the tag will be generated and where new fixes will be applied as part of the maintenance for the release."),(0,r.yg)("p",null,"The branch needs only to be created for feature releases, and not for patch releases like ",(0,r.yg)("inlineCode",{parentName:"p"},"2.3.1"),". For patch releases, go to the next step."),(0,r.yg)("p",null,"For example, when you create the ",(0,r.yg)("inlineCode",{parentName:"p"},"v2.3.0")," release, the branch ",(0,r.yg)("inlineCode",{parentName:"p"},"branch-2.3")," will be created; but for ",(0,r.yg)("inlineCode",{parentName:"p"},"v2.3.1"),", we\nkeep using the old ",(0,r.yg)("inlineCode",{parentName:"p"},"branch-2.3"),"."),(0,r.yg)("p",null,"In these instructions, a fictitious release ",(0,r.yg)("inlineCode",{parentName:"p"},"2.X.0")," is referred. Change the release version in the examples accordingly with the real version."),(0,r.yg)("p",null,"It is recommended to create a fresh clone of the repository to avoid any local files interfering in the process:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"git clone git@github.com:apache/pulsar.git\ncd pulsar\nexport PULSAR_PATH=$(pwd)\ngit checkout -b $VERSION_BRANCH origin/master\n")),(0,r.yg)("p",null,"Alternatively, you can use a git workspace to create a new, clean directory on your machine without needing to re-download the project."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"git worktree add ../pulsar-release-$VERSION_BRANCH $VERSION_BRANCH\ncd ../pulsar-release-$VERSION_BRANCH\nexport PULSAR_PATH=$(pwd)\n")),(0,r.yg)("p",null,"if you get an error that the branch is already checked out, go to that directory detach it from the branch. After this the above command should succeed"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"git checkout --detach HEAD\n")),(0,r.yg)("p",null,"After the release, you can cleanup the worktree in the main repository directory"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"git worktree remove ../pulsar-release-$VERSION_BRANCH\n")),(0,r.yg)("p",null,"If you created a new branch, update the ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/apache/pulsar/blob/master/.github/workflows/ci-owasp-dependency-check.yaml"},"CI - OWASP Dependency Check")," workflow so that it will run on the new branch."),(0,r.yg)("p",null,"Note that you should also stop the workflow for previous Pulsar versions that are EOL."),(0,r.yg)("h3",{id:"cherry-picking-changes-scheduled-for-the-release"},"Cherry-picking changes scheduled for the release"),(0,r.yg)("p",null,"Before proceeding, ensure that you have ",(0,r.yg)("a",{parentName:"p",href:"/contribute/setup-mergetool"},"set up a Git mergetool"),". This tool is essential for resolving merge conflicts that may arise during the cherry-picking process."),(0,r.yg)("p",null,"Use a search such as ",(0,r.yg)("inlineCode",{parentName:"p"},"is:merged is:pr label:release/3.0.3 -label:cherry-picked/branch-3.0")," to search for merged PRs that are scheduled for the release, but haven't yet been cherry-picked.\nIt is necessary to handle cherry-picks in the same order as they have been merged in the master branch. Otherwise there will be unnecessary merge conflicts to resolve."),(0,r.yg)("p",null,"Here's a shell script where the output that will ease cherry-picking from master branch:\nassumes ",(0,r.yg)("inlineCode",{parentName:"p"},"gawk")," is gnu awk. install ",(0,r.yg)("inlineCode",{parentName:"p"},"brew install gawk")," or ",(0,r.yg)("inlineCode",{parentName:"p"},"alias gawk=awk")," on Linux."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre"},'UPSTREAM=origin\ngit fetch $UPSTREAM\nRELEASE_NUMBER=3.0.3\nRELEASE_BRANCH=branch-3.0\nPR_QUERY="is:merged label:release/$RELEASE_NUMBER -label:cherry-picked/$RELEASE_BRANCH"\nPR_NUMBERS=$(gh pr list -L 100 --search "$PR_QUERY" --json number --jq \'["#"+(.[].number|tostring)] | join("|")\')\nALREADY_PICKED=$(git log --oneline -P --grep="$PR_NUMBERS" --reverse $RELEASE_BRANCH | gawk \'match($0, /\\(#([0-9]+)\\)/, a) {print substr(a[0], 2, length(a[0])-2)}\' | tr \'\\n\' \'|\' | sed \'s/|$//\')\nif [[ -n "$ALREADY_PICKED" ]]; then\n echo "** Already picked but not tagged as cherry-picked **"\n git log --color --oneline -P --grep="$PR_NUMBERS" --reverse $RELEASE_BRANCH | gawk \'match($0, /\\(#([0-9]+)\\)/, a) {print $0 " https://github.com/apache/pulsar/pull/" substr(a[0], 3, length(a[0])-3)}\'\nfi\necho "** Not cherry-picked from $UPSTREAM/master **"\ngit log --color --oneline -P --grep="$PR_NUMBERS" --reverse $UPSTREAM/master | { [ -n "$ALREADY_PICKED" ] && grep --color -v -E "$ALREADY_PICKED" || cat; } | gawk \'match($0, /\\(#([0-9]+)\\)/, a) {print $0 " https://github.com/apache/pulsar/pull/" substr(a[0], 3, length(a[0])-3)}\'\necho "Check https://github.com/apache/pulsar/pulls?q=is:pr+$(echo "$PR_QUERY" | tr \' \' \'+\')"\n')),(0,r.yg)("p",null,"this produces an output such as:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre"},"** Already picked but not tagged as cherry-picked **\n744b7af5fc4 [improve][broker] Support not retaining null-key message during topic compaction (#21578) (#21662) https://github.com/apache/pulsar/pull/21578\nb41013ba45c [improve][broker] defer the ownership checks if the owner is inactive (ExtensibleLoadManager) (#21857) https://github.com/apache/pulsar/pull/21857\na6fd517ee39 [improve][build] Add a default username in the image (#21695) https://github.com/apache/pulsar/pull/21695\nbbf6ddf9244 [fix] [client] Do no retrying for error subscription not found when disabled allowAutoSubscriptionCreation (#22078) https://github.com/apache/pulsar/pull/22078\n** Not cherry-picked from origin/master **\necd16d68e29 [fix][client] fix negative message re-delivery twice issue (#20750) https://github.com/apache/pulsar/pull/20750\n50007c343ad [fix][txn] Fix getting last message ID when there are ongoing transactions (#21466) https://github.com/apache/pulsar/pull/21466\ne81a20d667a [fix][broker] Avoid consumers receiving acknowledged messages from compacted topic after reconnection (#21187) https://github.com/apache/pulsar/pull/21187\n09559c5e661 [fix] [broker] Fix reader stuck when read from compacted topic with read compact mode disable (#21969) https://github.com/apache/pulsar/pull/21969\n48b4481969c [improve] [broker] Do not print an Error log when responding to `HTTP-404` when calling `Admin API` and the topic does not exist. (#21995) https://github.com/apache/pulsar/pull/21995\n861618a8120 [fix] [broker] Expire messages according to ledger close time to avoid client clock skew (#21940) https://github.com/apache/pulsar/pull/21940\n48c7e322fec [improve][admin] Expose the offload threshold in seconds to the amdin (#22101) https://github.com/apache/pulsar/pull/22101\n1c652f5519e [improve] [broker] Do not try to open ML when the topic meta does not exist and do not expect to create a new one. #21995 (#22004) https://github.com/apache/pulsar/pull/22004\n86079059890 [improve][broker] Cache the internal writer when sent to system topic. (#22099) https://github.com/apache/pulsar/pull/22099\n1b1cfb58f4e [fix] [broker] Enabling batch causes negative unackedMessages due to ack and delivery concurrency (#22090) https://github.com/apache/pulsar/pull/22090\n0c49cac105e [fix] [client] fix huge permits if acked a half batched message (#22091) https://github.com/apache/pulsar/pull/22091\n31ed115d0b5 [fix][sec] Add a check for the input time value (#22023) https://github.com/apache/pulsar/pull/22023\n30134966a18 [fix][test] fix test testSyncNormalPositionWhenTBRecover (#22120) https://github.com/apache/pulsar/pull/22120\n91de98ad456 [fix][test] Fix test testAsyncFunctionMaxPending (#22121) https://github.com/apache/pulsar/pull/22121\n")),(0,r.yg)("p",null,"It will speed up cherry-picking since you commit ids are there and there's also links to the PRs.\nA cherry-pick should be done in this order with ",(0,r.yg)("inlineCode",{parentName:"p"},"git cherry-pick -x COMMIT_ID"),".\nIt's possible that some dependent commits are necessary to be cherry-picked when you encounter a lot of merge conflicts in a case where they aren't expected."),(0,r.yg)("h3",{id:"update-project-version-and-tag"},"Update project version and tag"),(0,r.yg)("p",null,'During the release process, you are going to initially create "candidate" tags, that after verification and approval will get promoted to the "real" final tag.'),(0,r.yg)("p",null,"In this process, the maven version of the project will always be the final one."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'# Bump to the release version\ncd $PULSAR_PATH\n./src/set-project-version.sh $VERSION_WITHOUT_RC\n\n# Some version may not update the right parent version of `protobuf-shaded/pom.xml`, please double check it.\n\n# Commit\ngit commit -m "Release $VERSION_WITHOUT_RC" -a\n\n# Create a "candidate" tag\ngit tag -u $APACHE_USER@apache.org v$VERSION_RC -m "Release $VERSION_RC"\n\n# Verify that you signed your tag before pushing it:\ngit tag -v v$VERSION_RC\n\n# Push both the branch and the tag to Github repo\ngit push $UPSTREAM_REMOTE $VERSION_BRANCH\ngit push $UPSTREAM_REMOTE v$VERSION_RC\n')),(0,r.yg)("p",null,"If there's a need to restart the release with more commits, you can delete the tag."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"# only if you restart the release before it has been published for voting. Don't run this after that!\n# delete local tag\ngit tag -d v$VERSION_RC\n# delete tag in remote\ngit push $UPSTREAM_REMOTE :v$VERSION_RC\n")),(0,r.yg)("p",null,"For patch releases, the tag is like ",(0,r.yg)("inlineCode",{parentName:"p"},"2.3.1"),"."),(0,r.yg)("h3",{id:"build-release-artifacts"},"Build release artifacts"),(0,r.yg)("p",null,"Run the following command to build the artifacts:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"cd $PULSAR_PATH\nmvn clean install -DskipTests\n")),(0,r.yg)("p",null,"After the build, you should find the following tarballs, zip files, and the connectors directory with all the Pulsar IO nar files:"),(0,r.yg)("ul",null,(0,r.yg)("li",{parentName:"ul"},(0,r.yg)("inlineCode",{parentName:"li"},"distribution/server/target/apache-pulsar-2.X.0-bin.tar.gz")),(0,r.yg)("li",{parentName:"ul"},(0,r.yg)("inlineCode",{parentName:"li"},"distribution/offloaders/target/apache-pulsar-offloaders-2.X.0-bin.tar.gz")),(0,r.yg)("li",{parentName:"ul"},(0,r.yg)("inlineCode",{parentName:"li"},"distribution/shell/target/apache-pulsar-shell-2.X.0-bin.tar.gz")),(0,r.yg)("li",{parentName:"ul"},(0,r.yg)("inlineCode",{parentName:"li"},"distribution/shell/target/apache-pulsar-shell-2.X.0-bin.zip")),(0,r.yg)("li",{parentName:"ul"},"directory ",(0,r.yg)("inlineCode",{parentName:"li"},"distribution/io/target/apache-pulsar-io-connectors-2.X.0-bin"))),(0,r.yg)("admonition",{type:"note"},(0,r.yg)("p",{parentName:"admonition"},"The ",(0,r.yg)("em",{parentName:"p"},"apache-pulsar-shell")," artifacts are distributed beginning with release 2.11.0.")),(0,r.yg)("h3",{id:"check-licenses"},"Check licenses"),(0,r.yg)("p",null,"First, check that the ",(0,r.yg)("inlineCode",{parentName:"p"},"LICENSE")," and ",(0,r.yg)("inlineCode",{parentName:"p"},"NOTICE")," files cover all included jars for the bin package. You can use script to cross-validate ",(0,r.yg)("inlineCode",{parentName:"p"},"LICENSE")," file with included jars:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"cd $PULSAR_PATH\nsrc/check-binary-license.sh distribution/server/target/apache-pulsar-*-bin.tar.gz\n")),(0,r.yg)("p",null,"In some older branches, the script is called ",(0,r.yg)("inlineCode",{parentName:"p"},"src/check-binary-license")," instead of ",(0,r.yg)("inlineCode",{parentName:"p"},"src/check-binary-license.sh"),"."),(0,r.yg)("h3",{id:"create-and-publish-the-gpg-key-if-you-havent-already-done-this"},"Create and publish the GPG key if you haven't already done this"),(0,r.yg)("p",null,"If you haven't already done it, ",(0,r.yg)("a",{parentName:"p",href:"/contribute/create-gpg-keys"},"create and publish the GPG key"),". You will use the key to sign the release artifacts."),(0,r.yg)("p",null,"Before running the script below, make sure that the ",(0,r.yg)("inlineCode",{parentName:"p"},"<yourname>@apache.org")," code signing key is the default gpg signing key."),(0,r.yg)("p",null,"One way to ensure this is to create/edit file ",(0,r.yg)("inlineCode",{parentName:"p"},"~/.gnupg/gpg.conf")," and add a line:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre"},"default-key <key fingerprint>\n")),(0,r.yg)("p",null,"... where ",(0,r.yg)("inlineCode",{parentName:"p"},"<key fingerprint>")," should be replaced with the private key fingerprint for the ",(0,r.yg)("inlineCode",{parentName:"p"},"<yourname>@apache.org")," key. The key fingerprint can be found in ",(0,r.yg)("inlineCode",{parentName:"p"},"gpg -K")," output."),(0,r.yg)("p",null,"This can be automated with this command:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'# KEY_ID is in short format, subset key id visible in gpg -K\nKEY_ID=$(gpg --list-keys --with-colons $APACHE_USER@apache.org | egrep "^pub" | awk -F: \'{print $5}\')\necho "default-key $KEY_ID" >> ~/.gnupg/gpg.conf\n')),(0,r.yg)("h3",{id:"sign-and-stage-the-artifacts-to-local-svn-directory"},"Sign and stage the artifacts to local SVN directory"),(0,r.yg)("p",null,"The src and bin artifacts need to be signed and finally uploaded to the dist SVN repository for staging. This step should not run inside the $PULSAR_PATH."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"# make sure to run svn mkdir commmand in a different dir(NOT IN $PULSAR_PATH).\nmkdir ~/pulsar-svn-release-$VERSION_RC\ncd ~/pulsar-svn-release-$VERSION_RC\n\n# create an empty directory in the SVN server\nsvn mkdir --username $APACHE_USER -m \"Add directory for pulsar $VERSION_RC release\" https://dist.apache.org/repos/dist/dev/pulsar/pulsar-$VERSION_RC\n# checkout the empty directory\nsvn co https://dist.apache.org/repos/dist/dev/pulsar/pulsar-$VERSION_RC\n# cd into the directory\ncd pulsar-$VERSION_RC\n\n# stage the release artifacts\n$PULSAR_PATH/src/stage-release.sh .\n\n# Please check the size of the files in the `pulsar-2.X.0-candidate-1`.\n# If you build the artifacts without a clean workspace, the `apache-pulsar-2.X.0-src.tar.gz` files\n# may be too large to be unable to upload.\nls -ltra\ndu -ms *\n\n# Verify the artifacts are correctly signed have correct checksums:\n( for i in **/*.(tar.gz|zip|nar); do echo $i; gpg --verify $i.asc $i || exit 1 ; done )\n( for i in **/*.(tar.gz|zip|nar); do echo $i; shasum -a 512 -c $i.sha512 || exit 1 ; done )\n\n# don't commit and upload yet, there's a separate step for handling that\n")),(0,r.yg)("h3",{id:"validate-the-release-files"},"Validate the release files"),(0,r.yg)("p",null,"Then use instructions in ",(0,r.yg)("a",{parentName:"p",href:"/contribute/validate-release-candidate"},"verifying release candidates")," page to do some sanity checks on the produced binary distributions."),(0,r.yg)("p",null," Make sure to run Apache RAT to verify the license headers in the src package:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"cd /tmp\ntar -xvzf ~/pulsar-svn-release-$VERSION_RC/pulsar-$VERSION_RC/apache-pulsar-*-src.tar.gz\ncd apache-pulsar-$VERSION_WITHOUT_RC-src\nmvn apache-rat:check\n")),(0,r.yg)("h3",{id:"commit-and-upload-the-staged-files-in-the-local-svn-directory-to-asf-svn-server"},"Commit and upload the staged files in the local SVN directory to ASF SVN server"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'cd ~/pulsar-svn-release-$VERSION_RC/pulsar-$VERSION_RC\nsvn add *\nsvn ci -m "Staging artifacts and signature for Pulsar release $VERSION_RC"\n')),(0,r.yg)("h3",{id:"stage-maven-modules"},"Stage Maven modules"),(0,r.yg)("p",null,"Upload the artifacts to ASF Nexus:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'cd $PULSAR_PATH\n# Confirm if there are no other new dirs or files in the $PULSAR_PATH because all files in $PULSAR_PATH will be compressed and uploaded to ASF Nexus.\ngit status\n\n# add space before the "export APACHE_PASSWORD" so that the password doesn\'t get added to shell history\n export APACHE_PASSWORD="<MY_PASSWORD>"\nexport GPG_TTY=$(tty)\n# src/settings.xml from master branch to /tmp/mvn-apache-settings.xml since it\'s missing in some branches\ncurl -s -o /tmp/mvn-apache-settings.xml https://raw.githubusercontent.com/apache/pulsar/master/src/settings.xml\n# publish artifacts\nmvn deploy -DskipTests -Papache-release --settings /tmp/mvn-apache-settings.xml\n# publish org.apache.pulsar.tests:integration and it\'s parent pom org.apache.pulsar.tests:tests-parent\nmvn deploy -DskipTests -Papache-release --settings /tmp/mvn-apache-settings.xml -f tests/pom.xml -pl org.apache.pulsar.tests:tests-parent,org.apache.pulsar.tests:integration\n')),(0,r.yg)("admonition",{type:"note"},(0,r.yg)("p",{parentName:"admonition"},"The ",(0,r.yg)("inlineCode",{parentName:"p"},"GPG_TTY"),' environment variable must be set for all the following steps. Otherwise, some operations might fail by "gpg failed to sign the data".')),(0,r.yg)("p",null,"This will ask for the GPG key passphrase and then upload it to the staging repository."),(0,r.yg)("p",null,"Log in to the ASF Nexus repository at ",(0,r.yg)("a",{parentName:"p",href:"https://repository.apache.org"},"https://repository.apache.org")),(0,r.yg)("p",null,'Click on "Staging Repositories" on the left sidebar and then select the current Pulsar staging repo. This should be called something like ',(0,r.yg)("inlineCode",{parentName:"p"},"orgapachepulsar-XYZ"),"."),(0,r.yg)("p",null,'Add a version string such as "Apache Pulsar 3.0.4-candidate-1" to the clipboard with this command:'),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'printf "Apache Pulsar $VERSION_RC" |pbcopy\n')),(0,r.yg)("p",null,'Use the "Close" button to close the repository.'),(0,r.yg)("p",null,'Enter the version string in the description field before clicking "Confirm".'),(0,r.yg)("p",null,'This operation will take few minutes. Once complete click "Refresh" and now a link to the staging repository should be available, something like ',(0,r.yg)("a",{parentName:"p",href:"https://repository.apache.org/content/repositories/orgapachepulsar-XYZ"},"https://repository.apache.org/content/repositories/orgapachepulsar-XYZ")),(0,r.yg)("h3",{id:"stage-docker-images"},"Stage Docker images"),(0,r.yg)("p",null,"After that, the following images will be built and pushed to your own DockerHub account:"),(0,r.yg)("ul",null,(0,r.yg)("li",{parentName:"ul"},"pulsar"),(0,r.yg)("li",{parentName:"ul"},"pulsar-all")),(0,r.yg)("h4",{id:"release-before-pulsar-30"},"Release before Pulsar 3.0"),(0,r.yg)("p",null,"This is supported only on Intel platforms. On Mac Apple Silicon, you can run Linux amd64 in a virtual machine or a physical machine outside the Apple laptop and use ",(0,r.yg)("inlineCode",{parentName:"p"},"export DOCKER_HOST=tcp://x.x.x.x:port")," to use use the remote docker engine for building the docker image. Don't forward the TCP/IP connection over an unencrypted channel.\nYou can start a socket proxy with ",(0,r.yg)("inlineCode",{parentName:"p"},"socat TCP-LISTEN:2375,bind=0.0.0.0,reuseaddr,fork UNIX-CLIENT:/var/run/docker.sock")," inside the Linux Intel machine.\nFor running the Linux Intel VM on Mac Apple Silicon, you could use ",(0,r.yg)("inlineCode",{parentName:"p"},"limactl create --name=linux_amd64 --rosetta --arch x86_64")," to create a VM using ",(0,r.yg)("a",{parentName:"p",href:"https://lima-vm.io/"},"https://lima-vm.io/"),".\nHowever, it is simpler to do the release on a Linux arm64 / x86_64 VM directly."),(0,r.yg)("p",null,"Run the following commands on a Linux machine (or with Mac where DOCKER_HOST points to a Linux amd64/Intel machine):"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"cd $PULSAR_PATH/docker\n./build.sh\nDOCKER_USER=<your-username> DOCKER_PASSWORD=<your-password> DOCKER_ORG=<your-organization> ./publish.sh\n")),(0,r.yg)("h3",{id:"release-pulsar-30-and-later"},"Release Pulsar 3.0 and later"),(0,r.yg)("p",null,"If you are using a git worktree, the git hash won't get properly applied to the docker image tag.\nthe workaround is to replace the ",(0,r.yg)("inlineCode",{parentName:"p"},".git")," file in the directory with a symbolic link to the worktree git directory"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'# only when using git worktree\ncd $PULSAR_PATH\nif [[ -f .git ]]; then\n REAL_GITDIR=$(cat .git |awk \'{ print $2 }\')\n if [[ -d "$REAL_GITDIR" ]]; then\n mv .git .git~\n ln -s $REAL_GITDIR .git\n echo "Workaround in place"\n else\n echo "Could find gitdir in .git file"\n fi\nfi\n')),(0,r.yg)("p",null,"For creating and publishing the docker images, run the following commands:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"# ensure that you have the most recent base image locally\ndocker pull ubuntu:22.04\n\ncd $PULSAR_PATH\nDOCKER_USER=<your-dockerhub-username>\ndocker login -u $DOCKER_USER\nmvn install -DUBUNTU_MIRROR=http://azure.archive.ubuntu.com/ubuntu/ \\\n -DskipTests \\\n -Dmaven.gitcommitid.nativegit=true \\\n -Pmain,docker -Pdocker-push \\\n -Ddocker.platforms=linux/amd64,linux/arm64 \\\n -Ddocker.organization=$DOCKER_USER \\\n -pl docker/pulsar,docker/pulsar-all\n")),(0,r.yg)("h2",{id:"call-for-the-vote-to-release-a-version-based-on-the-release-candidate"},"Call for the vote to release a version based on the release candidate"),(0,r.yg)("p",null,"Start a voting thread on the dev mailing list. "),(0,r.yg)("p",null,"Here is a way to render the template for the voting email."),(0,r.yg)("p",null,"Set these shell variables"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'DOCKER_USER=<your-dockerhub-username>\nSTAGING_REPO="<enter staging repo from https://repository.apache.org/#stagingRepositories>"\nMY_NAME="Firstname Lastname"\nPREVIOUS_VERSION_WITHOUT_RC="3.0.3"\n')),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'echo "Go to https://hub.docker.com/r/$DOCKER_USER/pulsar/tags to find the layer URL for the pulsar image"\necho "Go to https://hub.docker.com/r/$DOCKER_USER/pulsar-all/tags to find the layer URL for the pulsar image"\n')),(0,r.yg)("p",null,"Set these additional shell variable after looking up the URLs"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'PULSAR_IMAGE_URL="<looked up in previous step>"\nPULSAR_ALL_IMAGE_URL="<looked up in previous step>"\n')),(0,r.yg)("p",null,"Set also these"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"PULSAR_IMAGE_NAME=\"$DOCKER_USER/pulsar:$VERSION_WITHOUT_RC-$(git rev-parse --short=7 v$VERSION_RC^{commit})\"\nPULSAR_ALL_IMAGE_NAME=\"$DOCKER_USER/pulsar-all:$VERSION_WITHOUT_RC-$(git rev-parse --short=7 v$VERSION_RC^{commit})\"\n# validate pulling, will take some time, you can skip this if you have a slow internet connection\ndocker pull $PULSAR_IMAGE_NAME\ndocker pull $PULSAR_ALL_IMAGE_NAME\n# check that images are about right, you can skip this if you have a slow internet connection\ndocker run --rm $PULSAR_IMAGE_NAME bash -c 'ls /pulsar/lib' |less\ndocker run --rm $PULSAR_ALL_IMAGE_NAME bash -c 'ls /pulsar/lib' |less\n")),(0,r.yg)("p",null,"Now you can render the template to the clipboard"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"tee >(pbcopy) <<EOF\nTo: dev@pulsar.apache.org\nSubject: [VOTE] Release Apache Pulsar $VERSION_WITHOUT_RC based on $VERSION_RC\n\nHello Apache Pulsar Community,\n\nThis is a call for the vote to release the Apache Pulsar version $VERSION_WITHOUT_RC based on $VERSION_RC.\n\nIncluded changes since the previous release:\nhttps://github.com/apache/pulsar/compare/v$PREVIOUS_VERSION_WITHOUT_RC...v$VERSION_RC\n\n*** Please download, test and vote on this release. This vote will stay open\nfor at least 72 hours ***\n\nOnly votes from PMC members are binding, but members of the community are\nencouraged to test the release and vote with \"(non-binding)\".\n\nNote that we are voting upon the source (tag), binaries are provided for\nconvenience.\n\nThe release candidate is available at:\nhttps://dist.apache.org/repos/dist/dev/pulsar/pulsar-$VERSION_RC/\n\nSHA-512 checksums:\n$(cat $HOME/pulsar-svn-release-$VERSION_RC/pulsar-$VERSION_RC/apache-pulsar-$VERSION_WITHOUT_RC-src.tar.gz.sha512 | sed 's|\\./||g')\n$(cat $HOME/pulsar-svn-release-$VERSION_RC/pulsar-$VERSION_RC/apache-pulsar-$VERSION_WITHOUT_RC-bin.tar.gz.sha512 | sed 's|\\./||g')\n\nMaven staging repo:\n$STAGING_REPO\n\nThe tag to be voted upon:\nv$VERSION_RC (commit $(git rev-parse v$VERSION_RC^{commit}))\nhttps://github.com/apache/pulsar/releases/tag/v$VERSION_RC\n\nPulsar's KEYS file containing PGP keys you use to sign the release:\nhttps://downloads.apache.org/pulsar/KEYS\n\nDocker images:\ndocker pull $PULSAR_IMAGE_NAME\n$PULSAR_IMAGE_URL\ndocker pull $PULSAR_ALL_IMAGE_NAME\n$PULSAR_ALL_IMAGE_URL\n\nPlease download the source package, and follow the README to build\nand run the Pulsar standalone service.\n\nMore advanced release validation instructions can be found at\nhttps://pulsar.apache.org/contribute/validate-release-candidate/\n\nThanks,\n\n$MY_NAME\nEOF\n")),(0,r.yg)("p",null,"The vote should be open for at least 72 hours (3 days). Votes from Pulsar PMC members will be considered binding, while anyone else is encouraged to verify the release and vote as well."),(0,r.yg)("p",null,"If the release is approved here with 3 +1 binding votes, you can then proceed to the next step. Otherwise, you should repeat the previous steps and prepare another release candidate to vote."),(0,r.yg)("h2",{id:"summarize-the-voting-for-the-release"},"Summarize the voting for the release"),(0,r.yg)("p",null,"Once the vote has been passed, you will need to send a result vote to ",(0,r.yg)("a",{parentName:"p",href:"mailto:dev@pulsar.apache.org"},"dev@pulsar.apache.org")," on the voting thread."),(0,r.yg)("p",null,"Message:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'tee >(pbcopy) <<EOF\nHello all,\n\nThe vote to release Apache Pulsar version ${VERSION_WITHOUT_RC} based on ${VERSION_RC} is now closed.\n\nThe vote PASSED with X binding "+1", Y non-binding "+1" and 0 "-1" votes:\n\n"+1" Binding votes:\n\n - <name>\n\n"+1" Non-Binding votes:\n\n - <name>\n\nI\'ll continue with the release process and the release announcement will follow shortly.\n\nThanks,\n<your name>\nEOF\n')),(0,r.yg)("h2",{id:"promote-the-release"},"Promote the release"),(0,r.yg)("p",null,"For commands below, you need to set the environment variables ",(0,r.yg)("inlineCode",{parentName:"p"},"VERSION_RC"),", ",(0,r.yg)("inlineCode",{parentName:"p"},"VERSION_WITHOUT_RC"),", ",(0,r.yg)("inlineCode",{parentName:"p"},"UPSTREAM_REMOTE")," and ",(0,r.yg)("inlineCode",{parentName:"p"},"APACHE_USER"),".\nPlease check the ",(0,r.yg)("a",{parentName:"p",href:"#env-vars"},"environment variables step")," for doing that."),(0,r.yg)("h3",{id:"publish-the-final-tag"},"Publish the final tag"),(0,r.yg)("p",null,"Create and push the final Git tag:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'git tag -u $APACHE_USER@apache.org v$VERSION_WITHOUT_RC v$VERSION_RC^{} -m "Release v$VERSION_WITHOUT_RC"\ngit push $UPSTREAM_REMOTE v$VERSION_WITHOUT_RC\n')),(0,r.yg)("h3",{id:"create-release-notes-in-github"},"Create release notes in GitHub"),(0,r.yg)("p",null,"Then, you can ",(0,r.yg)("a",{parentName:"p",href:"https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository#creating-a-release"},"create a GitHub release")," based on the tag."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'# open this URL and create release notes by clicking "Create release from tag"\necho https://github.com/apache/pulsar/releases/tag/v${VERSION_WITHOUT_RC}\n\n# cherry-picked changes template\necho "[Cherry-picked changes](https://github.com/apache/pulsar/pulls?q=is%3Apr+is%3Amerged+label%3Arelease%2F${VERSION_WITHOUT_RC}+label%3Acherry-picked%2F${VERSION_BRANCH}+sort%3Acreated-asc)"\n')),(0,r.yg)("ol",null,(0,r.yg)("li",{parentName:"ol"},'Open the above URL in a browser and create release notes by clicking "Create release from tag".'),(0,r.yg)("li",{parentName:"ol"},'Find "Previous tag: auto" in the UI above the text box and choose the previous release there.'),(0,r.yg)("li",{parentName:"ol"},'Click "Generate release notes".'),(0,r.yg)("li",{parentName:"ol"},"Review the generated release notes."),(0,r.yg)("li",{parentName:"ol"},"Since changes are cherry-picked, you will have to include a link such as ",(0,r.yg)("a",{parentName:"li",href:"https://github.com/apache/pulsar/pulls?q=is%3Apr+is%3Amerged+label%3Arelease%2F2.11.4+label%3Acherry-picked%2Fbranch-2.11+sort%3Acreated-asc"},"Cherry-picked changes"),". There's a ",(0,r.yg)("a",{parentName:"li",href:"/contribute/release-note-guide"},"separate guide for generating automated release notes"),"."),(0,r.yg)("li",{parentName:"ol"},'Unselect "Set as the latest release" (that should be only selected for the actual latest release of Pulsar)'),(0,r.yg)("li",{parentName:"ol"},'Click "Publish release".')),(0,r.yg)("p",null,"The ",(0,r.yg)("a",{parentName:"p",href:"/contribute/release-note-guide"},"Writing release notes"),' guide should be followed to generate a proper release notes. That is covered in the "Update the document" section.'),(0,r.yg)("h3",{id:"release-the-artifacts-on-svn"},"Release the artifacts on SVN"),(0,r.yg)("p",null,"Promote the artifacts on the release SVN repo ",(0,r.yg)("a",{parentName:"p",href:"https://dist.apache.org/repos/dist/release"},"https://dist.apache.org/repos/dist/release"),". Note that this repo is limited to PMC members, You may need a PMC member's help if you are not one:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'svn move -m "Release Apache Pulsar $VERSION_WITHOUT_RC" \\\n https://dist.apache.org/repos/dist/dev/pulsar/pulsar-$VERSION_RC \\\n https://dist.apache.org/repos/dist/release/pulsar/pulsar-$VERSION_WITHOUT_RC\n')),(0,r.yg)("h3",{id:"release-maven-modules"},"Release Maven modules"),(0,r.yg)("p",null,"Promote the Maven staging repository for release. Login to ",(0,r.yg)("a",{parentName:"p",href:"https://repository.apache.org"},"https://repository.apache.org"),' and select the staging repository associated with the RC candidate that was approved.\nDouble check the staging repository name from the release vote email.\nSelect the repository and click on "Release". Artifacts will now be made available on Maven central.'),(0,r.yg)("h3",{id:"release-docker-images"},"Release Docker images"),(0,r.yg)("p",null,"This step is performed by a Apache Pulsar PMC member. Please request help from a PMC member for completing this step."),(0,r.yg)("p",null,(0,r.yg)("inlineCode",{parentName:"p"},"regctl")," from ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/regclient/regclient"},"regclient")," is needed for copying multi-arch images. Install with ",(0,r.yg)("inlineCode",{parentName:"p"},"brew install regclient")," or with ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/regclient/regclient/blob/main/docs/install.md"},"other installation options")," of regclient. The benefit of ",(0,r.yg)("inlineCode",{parentName:"p"},"regctl")," over using ",(0,r.yg)("inlineCode",{parentName:"p"},"docker pull/tag/push")," is that it will handle copying both ",(0,r.yg)("inlineCode",{parentName:"p"},"amd64")," and the ",(0,r.yg)("inlineCode",{parentName:"p"},"arm64")," image."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-bash"},"RELEASE_MANAGER_DOCKER_USER=otheruser\nCANDIDATE_TAG=$VERSION_WITHOUT_RC\n\nregctl image copy ${RELEASE_MANAGER_DOCKER_USER}/pulsar:${CANDIDATE_TAG} apachepulsar/pulsar:$VERSION_WITHOUT_RC\nregctl image copy ${RELEASE_MANAGER_DOCKER_USER}/pulsar-all:${CANDIDATE_TAG} apachepulsar/pulsar-all:$VERSION_WITHOUT_RC\n")),(0,r.yg)("p",null,"Go to check the result:"),(0,r.yg)("ul",null,(0,r.yg)("li",{parentName:"ul"},(0,r.yg)("a",{parentName:"li",href:"https://hub.docker.com/r/apachepulsar/pulsar/tags"},"https://hub.docker.com/r/apachepulsar/pulsar/tags")),(0,r.yg)("li",{parentName:"ul"},(0,r.yg)("a",{parentName:"li",href:"https://hub.docker.com/r/apachepulsar/pulsar-all/tags"},"https://hub.docker.com/r/apachepulsar/pulsar-all/tags"))),(0,r.yg)("p",null,"Ensure that newer than 3.x images support both amd64 and arm64. Older 2.x images should be amd64 only."),(0,r.yg)("admonition",{type:"caution"},(0,r.yg)("p",{parentName:"admonition"},"This step is for the latest release only.")),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre"},"regctl image copy apachepulsar/pulsar:$VERSION_WITHOUT_RC apachepulsar/pulsar:latest\nregctl image copy apachepulsar/pulsar-all:$VERSION_WITHOUT_RC apachepulsar/pulsar-all:latest\n")),(0,r.yg)("h3",{id:"release-helm-chart"},"Release Helm Chart"),(0,r.yg)("admonition",{type:"caution"},(0,r.yg)("p",{parentName:"admonition"},"This step is for the latest ",(0,r.yg)("em",{parentName:"p"},"LTS")," release only")),(0,r.yg)("ol",null,(0,r.yg)("li",{parentName:"ol"},"Bump the image version in the Helm Chart: ",(0,r.yg)("a",{parentName:"li",href:"https://github.com/apache/pulsar-helm-chart/blob/master/charts/pulsar/values.yaml"},"charts/pulsar/values.yaml")),(0,r.yg)("li",{parentName:"ol"},"Bump the chart version and ",(0,r.yg)("inlineCode",{parentName:"li"},"appVersion")," in the Helm Chart to the released version: ",(0,r.yg)("a",{parentName:"li",href:"https://github.com/apache/pulsar-helm-chart/blob/master/charts/pulsar/Chart.yaml"},"charts/pulsar/Chart.yaml")),(0,r.yg)("li",{parentName:"ol"},"Send a pull request for reviews and get it merged."),(0,r.yg)("li",{parentName:"ol"},"Once it is merged, the chart will be automatically released to GitHub releases at ",(0,r.yg)("a",{parentName:"li",href:"https://github.com/apache/pulsar-helm-chart"},"https://github.com/apache/pulsar-helm-chart")," and updated to ",(0,r.yg)("a",{parentName:"li",href:"https://pulsar.apache.org/charts/index.yaml"},"https://pulsar.apache.org/charts/index.yaml"),".")),(0,r.yg)("h3",{id:"release-homebrew-libpulsar-package"},"Release Homebrew libpulsar package"),(0,r.yg)("p",null,"For 2.8, 2.9 and 2.10 releases, you should release the libpulsar package on Homebrew."),(0,r.yg)("admonition",{type:"caution"},(0,r.yg)("p",{parentName:"admonition"},"The C++ client is now developing in a ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/apache/pulsar-client-cpp"},"separated repo"),". You should check its own release guide if you're releasing version >= 3.0.0.")),(0,r.yg)("p",null,"Release a new version of libpulsar for Homebrew, You can follow the example ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/Homebrew/homebrew-core/pull/53514"},"here"),"."),(0,r.yg)("h3",{id:"release-python-client"},"Release Python client"),(0,r.yg)("p",null,"For 2.8, 2.9 and 2.10 releases, you should release the Python client."),(0,r.yg)("admonition",{type:"note"},(0,r.yg)("ol",{parentName:"admonition"},(0,r.yg)("li",{parentName:"ol"},"You need to create an account on PyPI: ",(0,r.yg)("a",{parentName:"li",href:"https://pypi.org/account/register/"},"https://pypi.org/account/register/")),(0,r.yg)("li",{parentName:"ol"},"Ask anyone that has been a release manager before to add you as a maintainer for pulsar-docker on PyPI"),(0,r.yg)("li",{parentName:"ol"},"Once you have completed the following steps in this section, you can check if the wheels are uploaded successfully in ",(0,r.yg)("a",{parentName:"li",href:"https://pypi.org/project/pulsar-client/#files"},"Download files"),". Remember to switch to the correct version in ",(0,r.yg)("a",{parentName:"li",href:"https://pypi.org/project/pulsar-client/#history"},"Release history"),"."))),(0,r.yg)("admonition",{type:"caution"},(0,r.yg)("p",{parentName:"admonition"},"Make sure you run following command at the release tag!")),(0,r.yg)("admonition",{type:"caution"},(0,r.yg)("p",{parentName:"admonition"},"The Python client is now developing in a ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/apache/pulsar-client-python"},"separated repo"),". You should check its own release guide if you're releasing version >= 3.0.0.")),(0,r.yg)("h4",{id:"linux"},"Linux"),(0,r.yg)("p",null,"There is a script that builds and packages the Python client inside Docker images:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"pulsar-client-cpp/docker/build-wheels.sh\n")),(0,r.yg)("p",null,"The wheel files will be left under ",(0,r.yg)("inlineCode",{parentName:"p"},"pulsar-client-cpp/python/wheelhouse"),". Make sure all the files have ",(0,r.yg)("inlineCode",{parentName:"p"},"manylinux")," in the filenames. Otherwise, those files will not be able to upload to PyPI."),(0,r.yg)("p",null,"Run the following command to push the built wheel files:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"cd pulsar-client-cpp/python/wheelhouse\npip install twine\ntwine upload pulsar_client-*.whl\n")),(0,r.yg)("h4",{id:"macos"},"macOS"),(0,r.yg)("p",null,"There is a script that builds and packages the Python client inside Docker images:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"pulsar-client-cpp/python/build-mac-wheels.sh\n")),(0,r.yg)("p",null,"The wheel files will be generated at each platform directory under ",(0,r.yg)("inlineCode",{parentName:"p"},"pulsar-client-cpp/python/pkg/osx/"),". Then you can run ",(0,r.yg)("inlineCode",{parentName:"p"},"twin upload")," to upload those wheel files."),(0,r.yg)("h2",{id:"update-the-document"},"Update the document"),(0,r.yg)("h3",{id:"release-notes"},"Release notes"),(0,r.yg)("p",null,"This step is for every release. Read the specific guide for ",(0,r.yg)("a",{parentName:"p",href:"/contribute/release-note-guide"},"writing release notes"),"."),(0,r.yg)("h3",{id:"swagger-files"},"Swagger files"),(0,r.yg)("p",null,"This step is for every release."),(0,r.yg)("p",null,"First, build swagger files from apache/pulsar repo at the released tag:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"mvn -ntp install -Pcore-modules,swagger,-main -DskipTests -DskipSourceReleaseAssembly=true -Dspotbugs.skip=true -Dlicense.skip=true\nPULSAR_PATH=$(pwd)\n")),(0,r.yg)("p",null,"Now, run the following script from the main branch of apache/pulsar-site repo:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"cd tools/pytools\npoetry install\npoetry run bin/rest-apidoc-generator.py --master-path=$PULSAR_PATH --version=$VERSION_WITHOUT_RC\n")),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'# commit files\n# move to pulsar-site root\ncd ../..\ngit add -u\ngit add static/swagger/$VERSION_WITHOUT_RC\ngit commit -m "update rest-apidoc for $VERSION_WITHOUT_RC"\n')),(0,r.yg)("p",null,"Read more on the manual of ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/apache/pulsar-site/tree/main/tools/pytools/README.md"},"pytools"),"."),(0,r.yg)("h3",{id:"javadoc"},"Javadoc"),(0,r.yg)("admonition",{type:"caution"},(0,r.yg)("p",{parentName:"admonition"},"This step is for feature releases only, unless you're sure that significant Javadoc fixes are made against the patch release.")),(0,r.yg)("p",null,"After publish Java libraries, run the following script from the main branch of apache/pulsar-site repo:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"cd tools/pytools\npoetry install\npoetry run bin/java-apidoc-generator.py $VERSION_WITHOUT_RC\n")),(0,r.yg)("p",null,"Once the docs are generated, you can add them and submit them in a PR. The expected doc output is:"),(0,r.yg)("ul",null,(0,r.yg)("li",{parentName:"ul"},(0,r.yg)("inlineCode",{parentName:"li"},"static/api/admin")),(0,r.yg)("li",{parentName:"ul"},(0,r.yg)("inlineCode",{parentName:"li"},"static/api/client")),(0,r.yg)("li",{parentName:"ul"},(0,r.yg)("inlineCode",{parentName:"li"},"static/api/pulsar-functions"))),(0,r.yg)("p",null,"Read more on the manual of ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/apache/pulsar-site/tree/main/tools/pytools/README.md"},"pytools"),"."),(0,r.yg)("h3",{id:"reference"},"Reference"),(0,r.yg)("admonition",{type:"caution"},(0,r.yg)("p",{parentName:"admonition"},"This step is for feature releases only, unless you're sure that significant reference fixes are made against the patch release.")),(0,r.yg)("p",null,"You can generate references of config and command-line tool by running the following script from the main branch of apache/pulsar-site repo:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"# build Pulsar distributions under /path/to/pulsar-2.X.0\ncd tools/pytools\npoetry install\npoetry run bin/reference-doc-generator.py --master-path=$PULSAR_PATH --version=$VERSION_WITHOUT_RC\n")),(0,r.yg)("p",null,"Once the docs are generated, you can add them and submit them in a PR. The expected doc output is ",(0,r.yg)("inlineCode",{parentName:"p"},"static/reference/2.X.x")),(0,r.yg)("p",null,"Read more on the manual of ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/apache/pulsar-site/tree/main/tools/pytools/README.md"},"pytools"),"."),(0,r.yg)("h2",{id:"update-docs-redirect"},"Update ",(0,r.yg)("inlineCode",{parentName:"h2"},"/docs")," redirect"),(0,r.yg)("p",null,(0,r.yg)("a",{parentName:"p",href:"https://pulsar.apache.org/docs"},"https://pulsar.apache.org/docs")," should redirect to the latest feature release documentation."),(0,r.yg)("p",null,"If you're working on a patch release for an older feature version of Pulsar, you can skip this step."),(0,r.yg)("p",null,"Otherwise, you should update the version in this file: ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/apache/pulsar-site/blob/26671a6ce02ed529eb26072846aedf14e4ab31a5/static/.htaccess#L19"},"https://github.com/apache/pulsar-site/blob/26671a6ce02ed529eb26072846aedf14e4ab31a5/static/.htaccess#L19")),(0,r.yg)("h2",{id:"update-docs-version-list-dropdown"},"Update ",(0,r.yg)("inlineCode",{parentName:"h2"},"/docs")," version list dropdown"),(0,r.yg)("p",null,"The dropdown should have the following items:"),(0,r.yg)("ul",null,(0,r.yg)("li",{parentName:"ul"},"Next"),(0,r.yg)("li",{parentName:"ul"},"Active versions ",(0,r.yg)("a",{parentName:"li",href:"/contribute/release-policy/#supported-versions"},"still in support")),(0,r.yg)("li",{parentName:"ul"},"Others")),(0,r.yg)("p",null,"LTS versions should be labeled this way: ",(0,r.yg)("inlineCode",{parentName:"p"},"<version> LTS"),"."),(0,r.yg)("img",{alt:"docs version dropdown",src:"/img/version-dropdown.png",width:"320px"}),(0,r.yg)("p",null,"If you're working on a patch release for an older feature version of Pulsar, you can skip this step."),(0,r.yg)("p",null,"Otherwise, you should update the dropdown version list in this file: ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/apache/pulsar-site/blob/main/src/theme/DocsVersionDropdownNavbarItem.js"},"https://github.com/apache/pulsar-site/blob/main/src/theme/DocsVersionDropdownNavbarItem.js")),(0,r.yg)("h2",{id:"announce-the-release"},"Announce the release"),(0,r.yg)("p",null,"Once the release artifacts are available in the Apache Mirrors and the website is updated, you need to announce the release. You should email to ",(0,r.yg)("a",{parentName:"p",href:"mailto:dev@pulsar.apache.org"},"dev@pulsar.apache.org"),", ",(0,r.yg)("a",{parentName:"p",href:"mailto:users@pulsar.apache.org"},"users@pulsar.apache.org"),", and ",(0,r.yg)("a",{parentName:"p",href:"mailto:announce@apache.org."},"announce@apache.org.")," Here is a sample content:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"tee >(pbcopy) <<EOF\nTo: dev@pulsar.apache.org, users@pulsar.apache.org, announce@apache.org\nSubject: [ANNOUNCE] Apache Pulsar $VERSION_WITHOUT_RC released\n\nThe Apache Pulsar team is proud to announce Apache Pulsar version $VERSION_WITHOUT_RC.\n\nPulsar is a highly scalable, low latency messaging platform running on\ncommodity hardware. It provides simple pub-sub semantics over topics,\nguaranteed at-least-once delivery of messages, automatic cursor management for\nsubscribers, and cross-datacenter replication.\n\nFor Pulsar release details and downloads, visit:\n\nhttps://pulsar.apache.org/download\n\nRelease Notes are at:\nhttps://pulsar.apache.org/release-notes/versioned/pulsar-$VERSION_WITHOUT_RC/\n\nWe would like to thank the contributors that made the release possible.\n\nRegards,\n\nThe Pulsar Team\nEOF\n")),(0,r.yg)("p",null,"Send the email in plain text mode since the ",(0,r.yg)("a",{parentName:"p",href:"mailto:announce@apache.org"},"announce@apache.org")," mailing list will reject messages with text/html content."),(0,r.yg)("p",null,'In Gmail, there\'s an option to set "Plain text mode" in the "\u22ee" menu.'),(0,r.yg)("h2",{id:"write-a-blog-post-optional"},"Write a blog post (optional)"),(0,r.yg)("p",null,"It is encouraged to write a blog post to summarize the features introduced in this release, especially for feature releases."),(0,r.yg)("p",null,"You can follow the example ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/apache/pulsar/pull/2308"},"here"),". Be aware that the source of blog is moved to ",(0,r.yg)("a",{parentName:"p",href:"https://github.com/apache/pulsar-site/tree/main/blog"},"here"),"."),(0,r.yg)("h2",{id:"remove-old-releases"},"Remove old releases"),(0,r.yg)("p",null,"Remove the old releases (if any). You only need the latest release there, and older releases are available through the Apache archive:"),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"# Get the list of releases\nsvn ls https://dist.apache.org/repos/dist/release/pulsar\n\n# Delete each release (except for the last one)\nsvn rm https://dist.apache.org/repos/dist/release/pulsar/pulsar-3.X.X\n")),(0,r.yg)("h2",{id:"move-to-next-version-in-pomxml"},"Move to next version in pom.xml"),(0,r.yg)("h3",{id:"feature-releases-master-branch"},"Feature releases (master branch)"),(0,r.yg)("p",null,"You need to move the master version to the next iteration ",(0,r.yg)("inlineCode",{parentName:"p"},"Y")," (",(0,r.yg)("inlineCode",{parentName:"p"},"X + 1"),")."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},"git checkout master\n./src/set-project-version.sh 3.Y.0-SNAPSHOT\ngit commit -a -s -m \"[cleanup][build] Bumped version to 3.Y.0-SNAPSHOT'\n")),(0,r.yg)("p",null,"Since this needs to be merged into ",(0,r.yg)("inlineCode",{parentName:"p"},"master"),", you need to follow the regular process and create a Pull Request on GitHub."),(0,r.yg)("h3",{id:"for-maintenance-branches"},"For maintenance branches"),(0,r.yg)("p",null,"After the release process, you should bump the project version and append it with ",(0,r.yg)("inlineCode",{parentName:"p"},"-SNAPSHOT"),"."),(0,r.yg)("pre",null,(0,r.yg)("code",{parentName:"pre",className:"language-shell"},'./src/set-project-version.sh x.x.x-SNAPSHOT\ngit add -u\ngit commit -m "Bump version to next snapshot version"\n')))}u.isMDXComponent=!0}}]);