NOTE: This plugin contains plugin conventions for building specmatic tools. This is only to be used by the specmatic core team to build tools under the
io.specmaticnamespace.
The Specmatic Gradle Plugin provides an all-in-one solution for automating obfuscation, creating shadow JARs, and publishing artifacts to Maven repositories. By configuring the specmatic block, specmatic developers can streamline their build process without manually handling these steps. Ideal for simplifying deployment pipelines in JVM-based projects.
- Auto signing/publishing of artifacts
- maven central
- maven local
- specmatic private repository (on github)
- any other supported URLs/repositories
- License checks
- Ensure that dependencies have a license that allows commercial use of Specmatic software (i.e. no copyleft licenses) without any encumbrance.
- Generate a license report that can be packaged in the distributable jar. This is legal requirement from licenses like Apache, BSD-3-Clause. These licenses have a clause that requires distributions of software to carry a notice, or attribution specified in the license.
- Pretty print test progress - uses https://github.com/radarsh/gradle-test-logger-plugin
- Publish artifacts and create GitHub releases
- Print task info and dependencies - uses https://gitlab.com/barfuin/gradle-taskinfo
- Print vulnerability scan reports - uses osv-scanner
- Creates a
version.propertiesandVersionInfo.ktfile in the${groupId}:${projectName}package. This contains details like the version number, git sha. That may be useful for--versionor just dumping the version at startup. - Ensure that java and kotlin compilation is forced to a configured and consistent version across projects.
- Ensure artifacts are reproducible.
- Pretty print any
execorjavaexectasks, along with their outputs - Auto-upgrade/migrated deprecated dependencies to newer dependencies.
- Better integration with sample repositories
- Run a build against sample projects and validate changes.
- Bump version of dependency in sample project. Ensure that the appropriate jar is checked into the sample repo.
- Conflict detection and resolution using a combination of
io.fuchs.gradle.classpath-collision-detector,org.gradlex.jvm-dependency-conflict-detection,org.gradlex.jvm-dependency-conflict-resolution. See https://github.com/REPLicated/classpath-collision-detector and https://gradlex.org/jvm-dependency-conflict-resolution/ for more details.
-
The following environment variables containing secrets are needed based on the requirements. This script will help you upload the relevant secrets by scanning your github workflows.
Variable(s) Purpose Maven Central ORG_GRADLE_PROJECT_mavenCentralUsernameUsername for Maven Central ORG_GRADLE_PROJECT_mavenCentralPasswordPassword for Maven Central Signing ORG_GRADLE_PROJECT_signingInMemoryKeyGPG private key for signing (ascii armoured/base64 encoded, without the leading/trailing -----BEGIN/END lines) ORG_GRADLE_PROJECT_signingInMemoryKeyIdGPG key ID (last 8 chars of hex hex key without the leading 0x)ORG_GRADLE_PROJECT_signingInMemoryKeyPasswordPassphrase for the GPG key Specmatic Private Repo SPECMATIC_REPOSILITE_USERNAMEUsername for Specmatic private repository SPECMATIC_REPOSILITE_TOKENPassword for Specmatic private repository ORG_GRADLE_PROJECT_${REPOSITORY_NAME}UsernameUsername for repository with specified name ORG_GRADLE_PROJECT_${REPOSITORY_NAME}PasswordPassword for repository with specified name Docker Hub No variables are needed for image publishing (after docker login). Ifreadme.docker.mdexists, setSPECMATIC_DOCKER_HUB_USERNAMEandSPECMATIC_DOCKER_HUB_TOKENto publish Docker Hub README content.
-
Edit
build.gradle[.kts]// in the root project only plugins { // version specified in settings.gradle & gradle.properties id("io.specmatic.gradle") }
-
Edit
settings.gradle[.kts]pluginManagement { val specmaticGradlePluginVersion = settings.extra["specmaticGradlePluginVersion"] as String plugins { id("io.specmatic.gradle") version(specmaticGradlePluginVersion) } repositories { gradlePluginPortal() mavenCentral() mavenLocal() val repos = mapOf( "specmaticReleases" to uri("https://repo.specmatic.io/releases"), "specmaticSnapshots" to uri("https://repo.specmatic.io/snapshots"), "specmaticPrivate" to uri("https://repo.specmatic.io/private"), ) repos.forEach { (repoName, repoUrl) -> maven { name = repoName url = repoUrl credentials { username = settings.extra.properties["reposilite.user"]?.toString() ?: System.getenv("SPECMATIC_REPOSILITE_USERNAME") password = settings.extra.properties["reposilite.token"]?.toString() ?: System.getenv("SPECMATIC_REPOSILITE_TOKEN") } } } } } -
Edit
gradle.propertiesand add the plugin versionspecmaticGradlePluginVersion=<PLUGIN_VERSION_HERE> -
Add the following to your
build.gradle[.kts]filespecmatic { // Set the JVM version. Currently defaults to 17 jvmVersion = JavaLanguageVersion.of(17) // Set the kotlin version to be used. Currently defaults to 2.3.10 kotlinVersion = "2.3.10" // Set the kotlin compiler version. Currently defaults to 1.9 kotlinApiVersion = KotlinVersion.KOTLIN_1_9 // List of sample projects that need validation before release, and bumping post release downstreamDependentProjects = listOf("project1", "project2") // List of publish tasks that need to be run on the release tag releasePublishTasks = listOf("publishTasks...") // replace certain dependencies with other dependencies versionReplacements = mapOf( "org.example.foo:deprecated" to "org.example.foo:shiny-thing:1.2.3" ) // Provide license details for any libraries that don't have license information in their POM. // if using groovy, you may need to prefix below lines with `it.XXX` instead licenseData { name = "net.researchgate:gradle-release" version = "3.1.0" projectUrl = "https://github.com/researchgate/gradle-release" license = "MIT" } // Choose one of: withOSSLibrary, withOSSApplication, withOSSApplicationLibrary, // withCommercialLibrary, withCommercialApplication, withCommercialApplicationLibrary withOSSApplication(rootProject) { // The main class, if publishing an application variant mainClass = "io.specmatic.ExampleApp" // Publish this to some repositories. Can be invoked multiple times. publishTo("internalRepo", "https://internal.repo.url/repository/maven-releases/", io.specmatic.gradle.extensions.RepoType.PUBLISH_ALL) // Publish this to maven central. Only use this on open source code. publishToMavenCentral() // Create a GitHub release. Upload any files generated by specified tasks. githubRelease { addFile("sourcesJar", "foo-sources-${version}.jar") } // Create a docker build/publish task. Pass any optional args to the docker build task. // The `--build-arg VERSION` is already passed as a default dockerBuild { // optional, uses the project name by default imageName = "foo" // any extra docker build args extraDockerArgs = listOf("...") // publish to docker hub under these orgs. First one will be considered the primary org, and a readme will be generated dockerOrgNames = listOf("specmatic", "znsio") // For GitHub Container Registry: // dockerOrgNames = listOf("ghcr.io/specmatic") } // Obfuscation is enabled by default, but you may pass additional proguard args https://www.guardsquare.com/manual/configuration/usage obfuscate("-some-arg") obfuscate("-more-args", "-some-more-args") // Shadowing is enabled, but you pass any additional shadowing options - https://gradleup.com/shadow/ shadow(prefix = "specmatic_foo") { minimize() // other options... } publish { // configure the pom and any other publication settings pom { name.set("Specmatic License Validation") description.set("Specmatic License parsing and validation library") url.set("https://specmatic.io") } } } } -
Setup your
.gitignore# Add the following to the .gitignore file src/main/gen-kt/ src/main/gen-resources/
-
Setup GitHub workflows. Best to copy/paste from existing workflows.
The plugin also supports the following Gradle project properties:
| Property | Values | Used by | Effect |
|---|---|---|---|
release.releaseVersion |
Version string (for example 1.2.3) |
Release pipeline tasks (release, preReleaseBump, createReleaseTag, createGithubRelease) that need the exact release tag/version. |
Release version to tag and publish. |
release.newVersion |
Version string (for example 1.2.4-SNAPSHOT) |
Post-release bump tasks (release, postReleaseBump) that move the repo back to the next development version. |
Version to set after release completes. |
allowSnapshotDependencies |
true / false |
validateSnapshotDependencies in release lifecycle, when you intentionally release while still depending on snapshots. |
When true, snapshot dependency check warns instead of failing. |
skipBranchCheck |
true / false |
Release git safety checks (pre-release validation in GitOperations) that normally enforce running from main. |
Skip "must be on main branch" validation. |
skipRepoDirtyCheck |
true / false |
Release git safety checks before bump/tag operations; useful only for exceptional/manual flows. | Skip clean working tree validation. |
skipIncomingOutgoingCheck |
true / false |
Release git safety checks that verify local and remote branches are in sync before tagging/pushing. | Skip ahead/behind remote validation. |
disableMavenCentralAutoPublish |
true / false |
Maven Central publish configuration (publishToMavenCentral(...)) during publication setup. |
When true, disables automatic Maven Central release finalization. |
skipVulnValidation |
true / false |
Vulnerability scanning tasks (vulnScanSBOMScan, vulnScanJarScan, vulnScanDockerScan) when you want reports without failing the build. |
When true, still scans but does not fail build for HIGH/CRITICAL findings. |
skipDownstreamClone |
true / false |
Downstream integration tasks (validateDownstreamProjects, bumpVersionsInDownstreamProjects, fetchArtifactsInDownstreamProjects) in environments where repos are already present. |
Skip cloning/pulling downstream repositories. |
specmatic.jar.timestamp |
Present / absent | Version-info generation used by packaged artifacts (VersionInfo/version.properties) when build-time metadata is desired. |
When present, embeds build timestamp metadata in generated version info. |
<moduleName>Version |
Version string (dynamic key) | Downstream integration tasks that need a module version to write/fetch/validate in dependent repos. | Version used for bump/fetch/validate downstream projects. Key is lower camel case of root project name + Version (for example specmaticGradlePluginVersion). |
functionalTestingHack |
true / false |
Release plugin test path (runReleaseLifecycleHooks) used by functional tests to avoid nested GradleBuild release hooks. |
Internal testing switch; bypasses normal release lifecycle hook execution. Avoid in real releases. |
generateAllJars |
true / false |
For debugging, will generate all jars (obfuscated, unobfuscated, shaded...). This is disabled by default for build performance |
To work around the dependency hell problem where multiple dependencies have the same class, but different versions, you
can use the detectCollisions task to detect the collisions. This will print a report of all the dependencies that have
collisions, and their versions. Additionally, this plugin wraps the org.gradlex.jvm-dependency-conflict-resolution
plugin that addresses conflict resolution for some popular
dependencies. For other dependencies, you can
use the following snippet in your project:
jvmDependencyConflicts {
patch {
// attach capabilities to multiple modules that offer the same capability
module("org.example:old-name") {
addCapability("org.example:some-feature")
}
module("org.example.somepackage:new-name") {
addCapability("org.example:some-feature")
}
}
// resolve the conflicts by selecting the highest version of the dependency
conflictResolution {
selectHighestVersion("org.example:some-feature")
}
}This plugin ensures that the published application variants use slf4j and logback as the default logging mechanism.
Logback dependencies are automatically added by the plugin. A default logback.xml is packaged that turns off all
logging by default. In addition, you should setup your application's main() function to call JULForwarder.forward()
to setup appropriate forwarding of JUL logging to SLF4J.
import io.specmatic.yourpackage.JULForwarder
object Main {
@JvmStatic
fun main(args: Array<String>) {
JULForwarder.forward()
// your application code here
}
}You may override the default logback configuration by creating a logback.xml file and executing the application via:
java -Dlogback.configurationFile=logback.xml -jar <jar-file>Here is a list of available tasks
| Task | Description |
|---|---|
| Other checks | |
detectCollisions |
Detects dependency collisions and prints a report. |
| License Checks | |
checkLicense |
Check if License could be used |
generateLicenseReport |
Generates license report for all dependencies of this project and its subprojects. |
| Publishing tasks | |
publishAllPublicationsToMavenCentralRepository |
Publishes all Maven publications produced by this project to the mavenCentral repository. |
publishAllPublicationsToSpecmaticPrivateRepository |
Publishes all Maven publications produced by this project to the specmaticPrivate repository. |
publishAllPublicationsToStagingRepository |
Publishes all Maven publications produced by this project to the staging repository. |
publishToMavenCentral |
Publishes to a staging repository on Sonatype OSS. |
| Release tasks | |
release |
Verify project, release, and update version to next. |
| Vulnerability tasks | |
vulnScanSBOMScan |
Scan for and print vulnerabilities in dependency tree (SBOM). |
vulnScanJarScan |
Scan for and print vulnerabilities by deep scanning inside each generated jar. |
vulnScanDockerScan |
Scan for and print vulnerabilities in docker image (created when dockerBuild is configured). |
| Docker tasks | |
dockerBuild |
Builds the docker image (for local use) |
dockerBuildxPublish |
Builds and publishes linux/amd64,linux/arm64 variants of the docker image |
| Downstream Project Validation | |
validateDownstreamProjects |
Validate downstream project(s) |
bumpVersionsInDownstreamProjects |
Bump versions in downstream project(s) |
fetchArtifactsInDownstreamProjects |
Fetch artifacts downstream project(s) |
| Internal tasks | |
createGithubRelease |
Create a Github release. This is already wired up when publishing a release. |
cyclonedxBom |
Generates a CycloneDX compliant Software Bill of Materials (SBOM). |
| Generated artifact(s) | Obfuscated | Fat/Shadowed/Shaded | Has dependencies in POM | Javadoc/Source Jars | Is executable | Purpose |
|---|---|---|---|---|---|---|
| OSSLibraryConfig | ||||||
${groupId}:${projectId} |
❌ | ❌ | ✅ | ✅ | ❌ | Publishing a library (specmatic-junit5, for e.g.) |
| OSSApplicationConfig | ||||||
${groupId}:${projectId} |
❌ | ✅ | ❌ | ✅ | ✅ | Publishing an application (specmatic-executable, for e.g.) |
| OSSApplicationLibraryConfig | ||||||
${groupId}:${projectId} |
❌ | ❌ | ✅ | ✅ | ✅ | Use the application code as a library (specmatic-executable, for e.g.) |
${groupId}:${projectId}-all |
❌ | ✅ | ❌ | ✅ | ✅ | Publishing an application (specmatic-executable-all, for e.g.) |
| CommercialLibraryConfig | ||||||
${groupId}:${projectId} |
✅ | ✅ | ❌ | ❌ | ✅ | Publish a commercial library, for use in other modules (license core, for e.g.) |
${groupId}:${projectId}-all-debug |
❌ | ✅ | ❌ | ❌ | ✅ | For local debugging, above jar, but unobfuscated |
${groupId}:${projectId}-min |
✅ | ❌ | ✅ | ❌ | ❌ | Obfuscated, but has dependencies in POM, for local debugging |
${groupId}:${projectId}-core-dont-use-this-unless-you-know-what-you-are-doing |
❌ | ❌ | ✅ | ❌ | ❌ | Original jar + original deps in the POM, for local debugging |
| CommercialApplicationConfig | ✅ | ✅ | ||||
${groupId}:${projectId} |
✅ | ✅ | ❌ | ❌ | ✅ | Publish this for end user consumption |
${groupId}:${projectId}-all-debug |
❌ | ✅ | ❌ | ❌ | ✅ | For local debugging |
| CommercialApplicationAndLibraryConfig | ✅ | ✅ | ||||
${groupId}:${projectId} |
✅ | ❌ | ✅ | ❌ | ❌ | Publish this for end user consumption as as library |
${groupId}:${projectId}-all |
✅ | ✅ | ❌ | ❌ | ✅ | Publish this for end user consumption as an executable |
${groupId}:${projectId}-all-debug |
❌ | ✅ | ❌ | ❌ | ✅ | For local debugging |