Release Process
This document describes how the CDS Tooling libraries get a version, publish snapshots and release. It is for a developer who sets up or debugs a library release. It states the behaviour of the develop branch of each repository on 2026-10-14. Where a Jenkinsfile and an older document disagree, the Jenkinsfile is right.
The older names "qs-tooling pattern" and "qs-tooling release process" mean this process. The reference implementation is webbloqs-proxies.
Overview
A release has three steps, each done by one Jenkins job.
- A push to
developmakescontinuous-deploypublish a snapshotX.Y.Z-SNAPSHOT.Nand tag itvX.Y.Z-SNAPSHOT.N. - A person starts
releasewith a snapshot tag. The job promotes the tested tarball of that snapshot to the release registry. The job does not rebuild. - The same
releasejob tagsvX.Y.Z, writes the release notes and the released version todevelop, and posts to Slack.
feature branch -> continuous-build-multi build, test, Check Changeset; publishes nothing
push to develop -> continuous-deploy X.Y.Z-SNAPSHOT.N to the snapshot registry, tag vX.Y.Z-SNAPSHOT.N
person starts -> release (TAG, VERSION) tarball of the snapshot to the release registry, tag vX.Y.Z, commit on develop
Registries:
- Snapshot registry: the npm registry that holds the snapshots.
- Release registry: the npm registry that holds the releases.
Version source
Changesets (.changeset/) define the next version. A developer adds a changeset to each pull request that changes a published package (pnpm add-changeset copies .changeset/_template). The stage Check Changeset in continuous-build-multi runs scripts/check-changeset.sh and sets the build to UNSTABLE when the branch changes a significant file without a changeset. Tests, Markdown, Jenkinsfiles, Dockerfiles, .build/, scripts/, configuration files and the lock file are not significant.
continuous-deploy reads the next version from pnpm exec changeset status --output=changeset-status.json. The job needs a local develop branch for this, so it runs git fetch origin develop:develop first. The output goes to a file and never to stdout, because changesets truncates its output target. The build image is build-container:nodejs-22-gradle-8-jdk21-jammy, because changeset status fails on Node 20.
The job picks the snapshot base version in this order:
- Pending changesets exist: the base is the version that
changeset statuspredicts. If the packages predict more than one version, the job fails. - No changesets, and
package.jsonshows a version that the release registry has not released (a manual bump): the base is thepackage.jsonversion. - No changesets, and
package.jsonshows a released version: the job builds and tests but publishes nothing. The build name reads#N - no snapshot.
If the base version already exists on the release registry, the job fails with already released — changesets and package.json disagree.
package.json on develop records the last released version, never the next one. Snapshots sort above the last release and below the release they preview, because their base is the predicted next version.
Snapshot publication
continuous-deploy runs on every new commit on develop. The stages are:
- Install dependencies and decide the base version (see Version source).
- Check that the base version is not released for any published package.
- Build and test (
pnpm dist). This stage runs also when nothing publishes, sodevelopstays verified. - Publish:
pnpm set-version X.Y.Z-SNAPSHOT.N, setpublishConfig.registryto the snapshot registry in every package, runpnpm publish-npm. - Run the end-to-end tests (
webbloqs-proxies,webbloqs-catalog). - Tag the built commit
vX.Y.Z-SNAPSHOT.Nand push the tag. - Delete old snapshots. The job keeps the newest 10 snapshots of each package (
cleanupArtifactoryNpmPackages). - Start the
qs-tooling-documentationcontinuous-deployjob without waiting (webbloqs-proxies,webbloqs-catalog,qs-design-tests,qs-magellan).
N is the highest N in the snapshot registry and in the remote git tags for the base version, plus 1 (webbloqs-proxies, webbloqs-catalog, qs-design-tests). vite-plugin-webbloqs and qs-app-shell use the registry only.
webbloqs-catalog also builds two Storybook images in this job. The image tags X.Y.Z-SNAPSHOT.N and prod-X.Y.Z-SNAPSHOT.N carry the same N as the npm snapshot. The development image deploys to the Quatico test environment.
Release promotion
release promotes a tested snapshot. Nothing is rebuilt: the published bits are the bits that were tested. The job has two parameters:
TAG(required): a snapshot tag, formatvX.Y.Z-SNAPSHOT.N. The job rejects any other format.VERSION(optional): the release version, formatX.Y.ZorX.Y.Z-RC.1. Empty means the base version of the tag. The job rejects any other format.
The first run of a job has no parameters (JENKINS-41929). The stage validate parameters fails that run on purpose. Start the job again with Build with Parameters.
The stages are:
- Check out the tag, detached.
.changeset/now holds exactly the changesets of this release, althoughdevelophas usually moved on. - Verify: no published package has the release version, and the git tag
vX.Y.Zdoes not exist onorigin. The job checks both before it publishes, because a published package cannot be unpublished. - Publish: for each published package,
npm packthe snapshot from the snapshot registry, unpack it, setversionandpublishConfig.registryinside the tarball, replace dependencies on the snapshot version of sibling packages with the release version, and runnpm publish --tag <dist-tag>. The dist-tag isnextfor a pre-release (a version with-) andlatestotherwise. The job then waits up to 300 seconds, in steps of 15 seconds, until the release registry serves the version. - Generate the release notes from the changesets at the tag with
scripts/generate-release-notes.ts --version. - Tag
vX.Y.Zand push the tag. - Record the release on
develop. This applies to a final release only. A pre-release leavesdevelopuntouched and leaves the changesets pending for the final release.- Check out
origin/develop. - Delete exactly the changeset files that the release consumed. Changesets added after the snapshot stay.
- Write
Release-Notes.md. - Run
pnpm set-version X.Y.Z.developrecords the released version, not a higher one: a bump would count twice against the next changeset. - Commit with the message
E: set version to X.Y.Z after releaseand pushHEAD:develop. The push retries 3 times with a rebase. A re-run that finds nothing to commit succeeds.
- Check out
- Delete old snapshots (same cleanup as in
continuous-deploy).
webbloqs-catalog adds a stage that promotes the Storybook image: it pulls prod-X.Y.Z-SNAPSHOT.N, tags it X.Y.Z and pushes it. The tarball rewrite in this repository also covers peerDependencies.
Jenkins jobs
The npm libraries have these jobs on the apps Jenkins. Three repositories (webbloqs-proxies, webbloqs-catalog, qs-magellan) also have validate-documentation-changes, which copies the repository documentation into qs-tooling-documentation and posts to #q_cds_tooling_activity.
| Job | Trigger | Does | Slack |
|---|---|---|---|
continuous-build-multi | Multibranch scan every minute: every branch except develop | Build, test, Check Changeset; publishes nothing | None. The job reports the Bitbucket build status only. |
continuous-deploy | Poll of develop every 2 minutes | Snapshot publication | Success and failure in #q_cds_tooling_activity |
release | Manual, with TAG and VERSION | Release promotion | Success in #q_cds_tooling, failure in #q_cds_tooling_activity |
continuous-build-multi and continuous-deploy report the Bitbucket build status (bitbucketCloud.markBuildAs…). Of the release jobs, only webbloqs-catalog and vite-plugin-webbloqs report it.
Exceptions:
vite-plugin-webbloqscontinuous-deployposts on success only when it published a snapshot.webbloqs-catalogreleasealso posts a plain success line to#q_cds_tooling_activity, in addition to the announcement in#q_cds_tooling.- The release announcement of
webbloqs-proxies,webbloqs-catalog,vite-plugin-webbloqsandqs-design-testscarries the release notes in a code block. - The job
continuous-build-multidiscovers branches, not pull requests. A pull request shows the status of its source branch build.
Job paths
The jobs of the npm libraries live in the folder cds-tooling/ of the apps Jenkins, at job/cds-tooling/job/<repo>/job/<job>/. A Job DSL file in the infrastructure repository of the apps Jenkins defines them.
The folder was named qs-tooling/ before the rename to cds-tooling/. Older tickets and documents use the old path.
Jobs outside that folder:
webbloqs-website/{continuous-build,continuous-deploy,release}quawebplf/qs-runtime-{core,aem,magnolia}/…(see Maven releases)
Repositories
"Follows" means: changesets define the version, continuous-deploy publishes X.Y.Z-SNAPSHOT.N, and release promotes a tarball.
| Repository | Status | How it differs |
|---|---|---|
webbloqs-proxies | Follows | Reference implementation. A pnpm workspace in which all published packages share one version. |
webbloqs-catalog | Follows | Adds Storybook images that carry the snapshot N and a promotion stage for them. A fixed group in .changeset/config.json versions 8 packages together. release also rewrites peerDependencies. |
qs-design-tests | Follows | Copy of the webbloqs-proxies pipeline. No end-to-end stage. Runs on the Node 24 build image. |
vite-plugin-webbloqs | Follows, with 3 differences | The counter N reads the registry only. release passes no --tag to npm publish, so npm applies its default dist-tag latest also to a pre-release. release does not check that the git tag vX.Y.Z is free before it publishes. |
qs-app-shell | Differs | No changesets: the snapshot base is the raw package.json version, and no Check Changeset stage exists. release generates no release notes, passes no --tag, and sets develop to the released version plus one patch (semver -i patch) in a commit e set version to … after release. |
qs-magellan | Differs | Gradle build with Java and npm packages, no changesets. The version comes from build.gradle.kts. continuous-deploy publishes X.Y.Z-SNAPSHOT.N to the Maven snapshot repository and the npm registry on every develop build. release checks out the snapshot tag and rebuilds (./gradlew build publish githubRelease); it does not promote. Java artifacts go to the Maven release repository. npm packages go to npmjs through a GitHub workflow that the tag push on the GitHub mirror starts. release writes nothing to develop. |
qs-tooling-documentation | Differs | Docker image of the documentation site, no npm package. continuous-deploy builds the image X.Y.<build number>-RC.0, deploys it to the test environment, tags it and starts release for that tag. release re-tags the image and deploys it to production. Both jobs post to #q_cds_tooling_activity only. |
webbloqs-website | Differs | Docker image, no npm package, jobs outside cds-tooling/. The image tag is <version>-SNAPSHOT.<build number>. release asks for a manual yes or no (7 days) and re-tags the image on yes. It posts to #q_cds_tooling on yes only. |
qs-runtime-core, qs-runtime-aem, qs-runtime-magnolia | Differ | Maven releases. See the next section. |
Maven releases
The web platform repositories qs-runtime-core, qs-runtime-aem and qs-runtime-magnolia use Maven and have no changesets. They do not use continuous-deploy and release as described above. Their release job is release-release (release in qs-runtime-magnolia).
Jobs, on the apps Jenkins in the folder quawebplf/<repo>/. They are not in cds-tooling/:
continuous-buildis a multibranch job over all branches exceptrelease/*anddevelop. It runsmvn clean install -DskipITs. It publishes nothing and posts no Slack message.release-release(releaseinqs-runtime-magnolia) is a multibranch job over the branchesrelease/*.continuous-deployexists inqs-runtime-magnoliaonly. It pollsdevelopevery 2 minutes, runsmvn deploywith-Denforce-snapshot-version, and runs Sonar.qs-runtime-coreandqs-runtime-aemhave no job that publishes snapshots.
A release:
- The job runs on a branch
release/*. It names the build after the git tag onHEADand fails whenHEADis not exactly at a tag (git describe --exact-match --abbrev=0 --tags). - The job validates with
-Denforce-release-version. The Maven enforcer rulerequireReleaseVersionrejects a SNAPSHOT version in the POM, so the POM on the release branch must carry the release version. - The job runs
mvn clean install -DskipITs, thenmvn deploy -P publishNpm. This rebuilds from the tag. - The job posts success to
#q_cds_toolingand failure to#q_cds_tooling_activity. It writes nothing todevelop.
The build images are maven:3.9.6-eclipse-temurin-17 (qs-runtime-core), maven:3.9.6-eclipse-temurin-8 (qs-runtime-aem) and build-container:node-20-mvn-java-17-bash (qs-runtime-magnolia).
Debugging
- No snapshot after a push: open the log of the stage
Determine snapshot target(prepare environmentinwebbloqs-proxies). The linenothing to snapshotmeans no changeset is pending andpackage.jsonshows a released version. Failed to find where HEAD diverged from develop: the fetchgit fetch origin develop:developfailed or did not run. The job must not ignore this error.predicts more than one next version: the packages in the workspace have different pending bumps. Fix.changeset/config.json(fixedorlinked) or the changesets.already released: the base version exists on the release registry. Add a changeset or bumppackage.json.tag already existsinrelease: the tagvX.Y.Zexists onorigin. Choose anotherVERSION.- Release is red after
Publish Artifacts to quatico-npm: the packages are public, so a new run with the same version fails atVerify version availability. Create the tag and the commit ondevelopby hand.
Known defects
The team tracks known defects and open phases of the release pipelines internally. The retired name qs-tooling in old tickets and documents means CDS Tooling.