SBK

Storage Benchmark Kit

SBK release publication

SBK uses one guarded GitHub Actions workflow for GitHub Release assets, GitHub Packages, Docker Hub, and the GitHub Container Registry. Publication is independent from qualification. releasecheck can prove a candidate, but .github/workflows/release.yml neither invokes it nor requires its output. Maven Central publication is an independent maintainer operation performed with the established local JReleaser configuration; the GitHub workflow never configures or invokes JReleaser. The legacy Gradle -Pmaven repository path is not used.

Maintainer release procedure

Qualification and publication are deliberately separate operations. Complete this checklist in order for a normal stable release:

  1. Update sbkVersion in gradle.properties, complete the matching release notes at .github/release-notes/<version>.md, merge the changes, and use a clean local checkout of the latest origin/master.
  2. Qualify that exact commit independently with the release profile:

    ./gradlew clean releasecheck \
      -Pprofile=release \
      -PreleaseInventory=/secure/sbk-release-inventory.properties \
      --no-daemon --rerun-tasks
    

    Preserve build/reports/release-qualification/qualification.json and the functional reports as release evidence. The publication workflow does not consume them, so the maintainer must verify that qualification covered the same commit that will be released.

  3. Assemble and inspect every publication artifact without publishing:

    ./gradlew releasePublicationDryRun --no-daemon --rerun-tasks
    # Linux:
    (cd build/release-assets && sha256sum -c SHA256SUMS)
    # macOS:
    (cd build/release-assets && shasum -a 256 -c SHA256SUMS)
    
  4. If Maven Central is part of the release, stage, verify, and deploy the seven core modules from the authorized JReleaser host using the commands in Maven Central publication. This operation is independent because its signing and Sonatype credentials never enter the GitHub workflow.
  5. Export the local Docker Hub and GitHub dispatch credentials, then invoke the guarded root task with the exact version confirmation:

    export DOCKER_USERNAME="<docker-user>"
    export DOCKER_PASSWORD="<docker-access-token>"
    export GITHUB_TOKEN="<actions-workflow-token>"
    VERSION=$(sed -n 's/^sbkVersion=//p' gradle.properties)
    ./gradlew publish \
      "-PreleaseConfirm=RELEASE-${VERSION}" \
      --no-daemon
    
  6. Monitor Actions > SBK Release until every job succeeds, then perform the post-release verification. The Gradle task returns after dispatch; a successful local exit does not by itself mean the asynchronous GitHub release completed.

Do not run step 5 from an unmerged commit. The Docker image records the local HEAD, while the workflow runs from master and rejects an image whose revision label differs from the dispatched commit. Do not insert clean between qualification and evidence retention unless the reports have first been copied outside build/.

Safe local dry run

The following command exercises every core-module Maven publish task against project-local repositories, validates the exact main/source/Javadoc/documentation artifact set that JReleaser will deploy, assembles the container context, and creates the complete release asset contract:

./gradlew clean releasePublicationDryRun \
  --no-daemon --rerun-tasks

It never creates or pushes a Git tag, GitHub Release, Maven package, Docker Hub image, or GHCR image. The former publish -Pgithub selector has been removed; GitHub Packages are part of the complete guarded release. Results are written to:

build/release-assets/
build/release-container/

The Gradle dry run prepares the Docker context but does not require a Docker daemon. The GitHub release workflow never runs or consumes releasecheck. Open Actions > SBK Release > Run workflow, enter the version, and keep dry_run=true to build the complete asset contract and execute native AMD64 and ARM64 images without performing a registry login or push.

The local dry run and GitHub workflow intentionally do not invoke jreleaserDeploy. JReleaser requires the maintainer’s private signing and Sonatype credentials and remains a separate Maven Central publication step.

Contracted GitHub Release assets

Every release contains:

sbk-<version>.zip
sbk-<version>.tar
sbm-<version>.zip
sbm-<version>.tar
sbk-web-console-<version>.zip
sbk-web-console-<version>.tar
sbk-agent-docs.tar.gz
<core-module>-<version>-sbom.cdx.json
<core-module>-<version>-sbom.cdx.xml
release-manifest.json
SHA256SUMS

release-manifest.json binds the version and Git commit to every payload checksum, supported container platform, image repository, and immutable container digest. SHA256SUMS also covers the manifest itself.

The root SBK distribution already contains the sbk, sbk-yal, sbk-gem, and sbk-gem-yal launchers. SBM and the Local Web Console have separate standalone distributions.

No drivers:* project is published as a standalone archive, Maven package, or CycloneDX project. The guarded workflow calls releasePublishCoreToGitHubPackages for GitHub Packages. Independent local JReleaser publication uses releaseStageCorePublications. Both paths use the same explicit allow-list that excludes drivers/; the scope verifier also fails if a driver ever applies maven-publish. Drivers remain runtime plugins inside the SBK distribution and container. Direct CycloneDX release files are generated only for the publishable core modules: PerL, SBK API, SBK Web Console transport, SBM, SBK-YAL, SBK-GEM, and SBK-GEM-YAL. sbk-api and sbk-web-console are included because they are required compile/runtime dependencies of the public SBK API used by external applications. BuildKit separately generates an image SBOM from the complete container filesystem, including the driver libraries actually bundled there.

Container publication

The local publish task first publishes one multi-architecture manifest to Docker Hub. The workflow validates independently built native linux/amd64 and linux/arm64 images, then copies the exact public Docker Hub manifest to:

kmgowda/sbk
ghcr.io/kmgowda/sbk

Every version receives the exact version tag. Stable releases also update the major-version and latest tags; prereleases never update stable aliases. The workflow verifies both registries against the same Buildx digest. The local Buildx publication emits SBOM/provenance attestations. The workflow signs the GHCR immutable repository digest with Cosign and GitHub OIDC; Docker Hub is verified by immutable digest without transmitting its credentials to GitHub.

The AMD64 validation job also reports HIGH and CRITICAL Trivy findings. This scan is advisory because the aggregate image intentionally bundles every enabled driver’s third-party SDK; findings must be assessed and remediated per driver without blocking publication of unrelated benchmark backends. Native image builds, launcher/version checks, and the real File benchmark remain mandatory for both architectures.

Authentication

GitHub workflow dispatch and the internal GitHub Packages publication resolve the GitHub identity from these sources:

  1. Gradle properties github.username and github.token, when Gradle is run directly;
  2. GITHUB_USER and GITHUB_TOKEN environment variables;
  3. the legacy GITHUB_USERNAME variable for compatibility; or
  4. workflow-specific RELEASE_GITHUB_USER and RELEASE_GITHUB_TOKEN secrets, falling back to the workflow actor and repository GITHUB_TOKEN.

Docker Hub is a different authentication service; a GitHub token cannot be used there. Set DOCKER_USERNAME and DOCKER_PASSWORD only in the local environment that invokes Gradle. The publishDockerHub task supplies the password through docker login --password-stdin, uses a temporary DOCKER_CONFIG, pushes the multi-architecture image, records its public immutable digest, and deletes the temporary configuration before Gradle exits. The supplied Docker identity must have push permission for kmgowda/sbk. Neither Docker credential is sent to GitHub or stored in the repository. The workflow receives only the non-secret public digest and copies that exact manifest to GHCR.

JReleaser does not use the GitHub credentials above for Maven Central and is never invoked by the GitHub workflow. Local invocations resolve its private TOML configuration from Gradle property jreleaser.configFile, environment variable JRELEASER_CONFIG_FILE, or the existing default ~/.jreleaser/config.toml, in that order. Keep this private configuration on the authorized Maven Central publication host; it is not a GitHub Actions secret.

The private TOML supplies JReleaser credentials and environment values. The root Gradle release configuration remains authoritative for the staging repository inventory and always registers PerL, SBK Web Console, SBK API, SBM, SBK-YAL, SBK-GEM, and SBK-GEM-YAL. Module Maven publication stages unsigned artifacts; JReleaser is the sole component that signs the Maven Central payload. Legacy Gradle signing properties must not create signMavenJavaPublication tasks.

Release qualification may independently use the real-host secrets documented in Release qualification. The publication workflow does not receive or require remote benchmark credentials.

Publishing a release

Dispatch the complete guarded release from Gradle:

VERSION=$(sed -n 's/^sbkVersion=//p' gradle.properties)
DOCKER_USERNAME="<docker-user>" \
DOCKER_PASSWORD="<docker-access-token>" \
GITHUB_TOKEN="<actions-workflow-token>" \
./gradlew publish \
  "-PreleaseConfirm=RELEASE-${VERSION}" \
  --no-daemon

The confirmation value must exactly match RELEASE-<sbkVersion>. The task always sends dry_run=false and dispatches .github/workflows/release.yml from master; therefore, it performs an actual release rather than a local dry run. Use -Pgithub.token=<token> instead of GITHUB_TOKEN when Gradle property-based authentication is preferred. This token authorizes only the GitHub Actions workflow dispatch and GitHub-owned publication steps.

Optional release controls are:

-PreleasePrerelease=true
-PreleaseResume=true

Both default to false. The task submits the following workflow inputs:

version=<exact sbkVersion from gradle.properties>
dry_run=false
prerelease=false
resume=false
dockerhub_digest=sha256:<immutable-public-digest>

The same inputs remain available through Actions > SBK Release > Run workflow when a browser-driven release is required. For an actual browser-driven publication, first run publishDockerHub locally with the same confirmation and credentials, then copy the resulting build/reports/release-publication/dockerhub-digest.txt value into the dockerhub_digest workflow input. A dry run does not require that digest.

The local Gradle task publishes Docker Hub before dispatch. The workflow builds the complete contracted asset set without running or depending on releasecheck, validates both container architectures, copies the exact image to GHCR, invokes releasePublishCoreToGitHubPackages for GitHub Packages, creates an annotated tag, creates a draft GitHub Release, uploads and compares every asset, and publishes the release last with the versioned details from .github/release-notes/<version>.md. It does not configure or execute JReleaser.

If publication stops after the exact tag or draft release is created, inspect the partial state before rerunning with resume=true. Resume is accepted only for the same tag and commit, and existing assets must be byte-identical. GitHub Packages versions are also immutable. A retry accepts HTTP 409 only after authenticated verification proves that every required artifact exists for all seven core modules and that each POM, executable JAR, and sources JAR is byte-identical to the locally staged publication. Javadoc and documentation JARs must exist and be non-empty; generated Javadoc can vary across JDK patch releases, and the documentation JAR may contain newer release-operations guidance added while recovering the same immutable code release.

If Docker Hub publication succeeds but workflow dispatch or a later workflow job fails, do not delete or overwrite the version tag. Inspect the public digest written to build/reports/release-publication/dockerhub-digest.txt and the partial GitHub state. Correct the failure and rerun with -PreleaseResume=true; the workflow accepts only the same tag, commit, byte-identical assets, and immutable image digest.

Maven Central publication

Publish the Maven Central coordinates independently from an authorized host:

./gradlew verifyCorePublicationStaging jreleaserConfig --no-daemon
JRELEASER_MAVENCENTRAL_STAGE=FULL ./gradlew jreleaserDeploy --no-daemon

JReleaser resolves the private configuration described above. Its source default remains UPLOAD; setting the stage to FULL is the explicit Central release operation.

The published coordinates remain io.github.kmgowda.sbk:<module>:<version>. External projects can therefore build custom storage benchmarks without an SBK source checkout, for example with sbk-api, sbk-gem, or sbm from Maven Central. The same coordinates may be resolved from GitHub Packages after adding its repository and credentials. See the SBK examples repository.

Post-release verification

Verify the public state rather than relying only on the workflow conclusion:

VERSION=$(sed -n 's/^sbkVersion=//p' gradle.properties)
git fetch --tags origin
git rev-list -n 1 "v${VERSION}"
gh release view "v${VERSION}" \
  --json isDraft,isPrerelease,tagName,targetCommitish,url,assets
docker buildx imagetools inspect "kmgowda/sbk:${VERSION}"
docker buildx imagetools inspect "ghcr.io/kmgowda/sbk:${VERSION}"

Confirm that:

Record the release URL, workflow-run URL, tag commit, container digest, and Maven Central result with the retained qualification evidence.