Skip to main content

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.

  1. A push to develop makes continuous-deploy publish a snapshot X.Y.Z-SNAPSHOT.N and tag it vX.Y.Z-SNAPSHOT.N.
  2. A person starts release with a snapshot tag. The job promotes the tested tarball of that snapshot to the release registry. The job does not rebuild.
  3. The same release job tags vX.Y.Z, writes the release notes and the released version to develop, 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:

  1. Pending changesets exist: the base is the version that changeset status predicts. If the packages predict more than one version, the job fails.
  2. No changesets, and package.json shows a version that the release registry has not released (a manual bump): the base is the package.json version.
  3. No changesets, and package.json shows 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:

  1. Install dependencies and decide the base version (see Version source).
  2. Check that the base version is not released for any published package.
  3. Build and test (pnpm dist). This stage runs also when nothing publishes, so develop stays verified.
  4. Publish: pnpm set-version X.Y.Z-SNAPSHOT.N, set publishConfig.registry to the snapshot registry in every package, run pnpm publish-npm.
  5. Run the end-to-end tests (webbloqs-proxies, webbloqs-catalog).
  6. Tag the built commit vX.Y.Z-SNAPSHOT.N and push the tag.
  7. Delete old snapshots. The job keeps the newest 10 snapshots of each package (cleanupArtifactoryNpmPackages).
  8. Start the qs-tooling-documentation continuous-deploy job 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, format vX.Y.Z-SNAPSHOT.N. The job rejects any other format.
  • VERSION (optional): the release version, format X.Y.Z or X.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:

  1. Check out the tag, detached. .changeset/ now holds exactly the changesets of this release, although develop has usually moved on.
  2. Verify: no published package has the release version, and the git tag vX.Y.Z does not exist on origin. The job checks both before it publishes, because a published package cannot be unpublished.
  3. Publish: for each published package, npm pack the snapshot from the snapshot registry, unpack it, set version and publishConfig.registry inside the tarball, replace dependencies on the snapshot version of sibling packages with the release version, and run npm publish --tag <dist-tag>. The dist-tag is next for a pre-release (a version with -) and latest otherwise. The job then waits up to 300 seconds, in steps of 15 seconds, until the release registry serves the version.
  4. Generate the release notes from the changesets at the tag with scripts/generate-release-notes.ts --version.
  5. Tag vX.Y.Z and push the tag.
  6. Record the release on develop. This applies to a final release only. A pre-release leaves develop untouched 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. develop records 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 release and push HEAD:develop. The push retries 3 times with a rebase. A re-run that finds nothing to commit succeeds.
  7. 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.

JobTriggerDoesSlack
continuous-build-multiMultibranch scan every minute: every branch except developBuild, test, Check Changeset; publishes nothingNone. The job reports the Bitbucket build status only.
continuous-deployPoll of develop every 2 minutesSnapshot publicationSuccess and failure in #q_cds_tooling_activity
releaseManual, with TAG and VERSIONRelease promotionSuccess 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-webbloqs continuous-deploy posts on success only when it published a snapshot.
  • webbloqs-catalog release also 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-webbloqs and qs-design-tests carries the release notes in a code block.
  • The job continuous-build-multi discovers 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.

RepositoryStatusHow it differs
webbloqs-proxiesFollowsReference implementation. A pnpm workspace in which all published packages share one version.
webbloqs-catalogFollowsAdds 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-testsFollowsCopy of the webbloqs-proxies pipeline. No end-to-end stage. Runs on the Node 24 build image.
vite-plugin-webbloqsFollows, with 3 differencesThe 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-shellDiffersNo 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-magellanDiffersGradle 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-documentationDiffersDocker 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-websiteDiffersDocker 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-magnoliaDifferMaven 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-build is a multibranch job over all branches except release/* and develop. It runs mvn clean install -DskipITs. It publishes nothing and posts no Slack message.
  • release-release (release in qs-runtime-magnolia) is a multibranch job over the branches release/*.
  • continuous-deploy exists in qs-runtime-magnolia only. It polls develop every 2 minutes, runs mvn deploy with -Denforce-snapshot-version, and runs Sonar. qs-runtime-core and qs-runtime-aem have no job that publishes snapshots.

A release:

  1. The job runs on a branch release/*. It names the build after the git tag on HEAD and fails when HEAD is not exactly at a tag (git describe --exact-match --abbrev=0 --tags).
  2. The job validates with -Denforce-release-version. The Maven enforcer rule requireReleaseVersion rejects a SNAPSHOT version in the POM, so the POM on the release branch must carry the release version.
  3. The job runs mvn clean install -DskipITs, then mvn deploy -P publishNpm. This rebuilds from the tag.
  4. The job posts success to #q_cds_tooling and failure to #q_cds_tooling_activity. It writes nothing to develop.

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 environment in webbloqs-proxies). The line nothing to snapshot means no changeset is pending and package.json shows a released version.
  • Failed to find where HEAD diverged from develop: the fetch git fetch origin develop:develop failed 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 (fixed or linked) or the changesets.
  • already released: the base version exists on the release registry. Add a changeset or bump package.json.
  • tag already exists in release: the tag vX.Y.Z exists on origin. Choose another VERSION.
  • Release is red after Publish Artifacts to quatico-npm: the packages are public, so a new run with the same version fails at Verify version availability. Create the tag and the commit on develop by 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.