Apache Sling Committer CLI tool
July 20, 2026 · View on GitHub
Apache Sling Committer CLI tool
This module is part of the Apache Sling project.
This module provides a command-line tool which automates various Sling development tasks. The tool is packaged as a docker image.
Prerequisites
Before releasing you need the following in place. These are split between the Maven side (used by
mvn release:prepare/release:perform in the project being released) and the CLI side (used by
this Docker tool).
1. ASF credentials for the CLI (docker-env)
To make credentials available to the docker image, use a docker env file. A sample file is stored at
docker-env.sample; copy it to docker-env and fill in your own information. At minimum it must
provide your ASF credentials (these are read by the CLI for Nexus, JIRA, Whimsy and the mailing
lists):
ASF_USERNAME=your-apache-id
ASF_PASSWORD=your-apache-password
2. GPG signing key
- Generate a code-signing key and publish it:
gpg --send-keys <KEY_ID>tokeys.openpgp.org, and add its public block to the SlingKEYSfile at https://dist.apache.org/repos/dist/release/sling/KEYS (PMC members can commit there directly). - Note the key id (e.g.
0A1B2C3D4E5F6789) — it goes intosettings.xmlbelow.
3. Maven ~/.m2/settings.xml
Recent Apache/Sling parent POMs use maven-gpg-plugin 3.x, which changed how the GPG passphrase is
supplied. The old approach of a <gpg.passphrase> property in the apache-release profile is no
longer used — the plugin now reads the passphrase from a server whose id is given by
gpg.passphraseServerId (default: gpg.passphrase) and decrypts it via settings-security.xml.
<settings>
<servers>
<!-- ASF Nexus credentials (encrypted, see settings-security.xml) -->
<server>
<id>apache.snapshots.https</id>
<username>your-apache-id</username>
<password>{ENCRYPTED}</password>
</server>
<server>
<id>apache.releases.https</id>
<username>your-apache-id</username>
<password>{ENCRYPTED}</password>
</server>
<!-- maven-gpg-plugin 3.x reads the passphrase from THIS server id (gpg.passphraseServerId
defaults to "gpg.passphrase") and decrypts it via settings-security.xml. -->
<server>
<id>gpg.passphrase</id>
<passphrase>{ENCRYPTED}</passphrase>
</server>
</servers>
<profiles>
<profile>
<id>apache-release</id>
<properties>
<apache.availid>your-apache-id</apache.availid>
<!-- required so gpg does not try to open an interactive pinentry dialog -->
<gpg.pinentryMode>loopback</gpg.pinentryMode>
<!-- the signing key id from step 2 -->
<gpg.keyname>YOUR_KEY_ID</gpg.keyname>
<!-- SMTP host used by the parent POM's announcement tooling -->
<smtp.host>smtp.gmail.com</smtp.host>
</properties>
</profile>
</profiles>
</settings>
Encrypt the passwords/passphrase with mvn --encrypt-password (server passwords) and store the master
password in ~/.m2/settings-security.xml:
<settingsSecurity>
<master>{ENCRYPTED_MASTER_PASSWORD}</master>
</settingsSecurity>
Building
The Docker image (apache/sling-cli:latest) is produced by the docker:build goal of the
fabric8 docker-maven-plugin. The image bundles the project jar via the
generated *-app.slingfeature descriptor; the slingfeature-maven-plugin resolves the project's
own bundle from the reactor, so the image always contains the jar built in the same invocation.
A single command builds (and tests) the project and the image:
mvn clean package docker:build
No prior mvn install is needed. The docker:build execution is also bound to the package
phase, so the CI build (mvn package) builds the image too.
To confirm the image contains the expected commands:
docker run --env-file=./docker-env apache/sling-cli release help
Launching
After building, run the image with:
docker run --env-file=./docker-env apache/sling-cli
This invocation produces a list of available commands.
Commands
The commands can be executed in 3 different modes:
DRY_RUN(default mode) - commands only list their output without performing any actions on the user's behalfINTERACTIVE- commands list their output but ask for user confirmation when it comes to performing an action on the user's behalfAUTO- commands list their output and assume that all questions are provided the default answers when it comes to performing an action on the user's behalf
To select a non-default execution mode provide the mode as an argument to the command:
docker run -it --env-file=./docker-env apache/sling-cli release prepare-email --repository=$STAGING_REPOSITORY
--execution-mode=INTERACTIVE
Note that for running commands in the INTERACTIVE mode you need to run the Docker container in interactive mode with a pseudo-tty
attached (e.g. docker run -it ...).
Release workflow
The full release lifecycle has manual Maven steps (run in the project being released) and CLI
steps (run via this Docker tool). The CLI commands take the numeric staging repository id via
-r/--repository (e.g. 3103 for orgapachesling-3103). Append -x AUTO to actually perform an
action (the default mode is DRY_RUN).
Manual steps in the project being released (Maven)
These are not part of this tool — run them in the module you are releasing, with the prerequisites from the section above configured:
-
Make sure the parent POM is up to date and the build is green (
mvn clean verify). -
Dry-run the release and check that only
<version>/<scm>change:mvn release:prepare -DdryRun=true mvn release:clean -
Deploy a snapshot and confirm
META-INF/LICENSEandMETA-INF/NOTICEare in the jar:mvn deploy -
Prepare and perform the release (creates the tag, bumps to the next SNAPSHOT, signs and stages the artifacts to Nexus):
mvn release:prepare mvn release:performNote the staging repository id from the
release:performoutput (a line like…/orgapachesling-1087). If it scrolls past,release list(below) shows open repos too.
CLI steps (this tool)
After release:perform has staged the artifacts, drive the rest with the CLI:
-
Find the staging repository id if you did not capture it.
release listshows every staging repo with its[open]/[closed]state and description; a freshly staged one is[open]:docker run --env-file=./docker-env apache/sling-cli release list -
Close the staging repository. The description is derived automatically from the staged POM's
<name>+<version>(e.g. Apache Sling Feature Model Launcher 1.3.6) by browsing the repository content, so it works even though an open repository is not yet in the Lucene index:docker run --env-file=./docker-env apache/sling-cli release close-staging --repository=$STAGING_REPOSITORY_ID --execution-mode=AUTO -
Verify the artifacts' signatures, hashes and CI status:
docker run --env-file=./docker-env apache/sling-cli release verify --repository=$STAGING_REPOSITORY_ID -
Generate the vote email:
docker run --env-file=./docker-env apache/sling-cli release prepare-email --repository=$STAGING_REPOSITORY_ID --execution-mode=AUTO -
After the 72h vote, tally the votes and generate the result email. PMC membership is detected automatically from your ASF id: if you are a PMC member the email says you will copy the release to the dist directory yourself; otherwise it asks a PMC member to perform the dist upload:
docker run --env-file=./docker-env apache/sling-cli release tally-votes --repository=$STAGING_REPOSITORY_ID --execution-mode=AUTO -
Finalize the release (post successful vote). This runs, in order: promote to Maven Central, create the next Jira version, release the current Jira version, and update the Apache Reporter:
docker run --env-file=./docker-env apache/sling-cli release finalize --repository=$STAGING_REPOSITORY_ID --execution-mode=AUTOWhen the current user is detected as a PMC member,
finalizeadditionally publishes todist.apache.org(requiressubversion, which is bundled in the image). The previous version to remove fromdist/releaseis deduced automatically from the directory contents, so no extra flag is needed:docker run --env-file=./docker-env apache/sling-cli release finalize --repository=$STAGING_REPOSITORY_ID --execution-mode=AUTOPMC membership is determined from your ASF id (via Whimsy). A non-PMC committer's
finalizeskips the dist upload and thetally-votesresult email asks a PMC member to perform it.
If the vote does not pass, drop the staging repository:
docker run --env-file=./docker-env apache/sling-cli release drop --repository=$STAGING_REPOSITORY_ID --execution-mode=AUTO
Command reference
| Command | Description |
|---|---|
release list | List closed staging repositories |
release close-staging -r <id> | Close an open staging repo, setting the description from the staged POM |
release verify -r <id> | Download and verify artifact signatures, hashes and CI status |
release prepare-email -r <id> | Generate (and send) the [VOTE] email |
release tally-votes -r <id> | Count votes and generate the [RESULT] email (PMC membership auto-detected; non-PMC email asks a PMC member to do the dist upload) |
release promote -r <id> | Promote a closed staging repo to Maven Central |
release update-dist -r <id> | Move artifacts to dist.apache.org (PMC only); previous version auto-deduced, override with --previous-version <v> |
release finalize -r <id> | Promote + Jira + Reporter in one step; also updates dist.apache.org when you are a PMC member |
release drop -r <id> | Drop a staging repository (failed vote / cleanup) |
release create-new-jira-version -r <id> | Create the next Jira version and move unresolved issues |
release release-jira-version -r <id> | Mark the Jira version as released and close fixed issues |
release update-reporter -r <id> | Register the release with the Apache Reporter System |
release update-local-site -r <id> | Generate the website update (diff only for now) |
Assumptions
This tool assumes that the name of the staging repository matches the one of the version in Jira. For instance, the staging repositories are usually named Apache Sling Foo 1.2.0. It is then expected that the Jira version is named Foo 1.2.0. Otherwise the link between the staging repository and the Jira release can not be found.
It is allowed for staging repository names to have an RC suffix, which may include a number, so that RC, RC1, RC25 are all valid suffixes.
