Cleanroom's Conventions. Here you can find how a Cleanroom's project is to behave.
Contains the following conventional files.
LICENSEHEADERlicenses/free/LICENSE,licenses/free/HEADER(MIT variant)licenses/open/LICENSE,licenses/open/HEADER(LGPLv3 variant)checkstyle.xmlformatj.tomlcliff.toml.editorconfig.gitattributes.gitignore
Published plugin IDs:
| Plugin ID | Purpose |
|---|---|
com.cleanroommc.conventions.settings |
Settings plugin: repositories and optional Foojay |
com.cleanroommc.conventions |
Applies every project convention plugin |
com.cleanroommc.conventions.base |
Versioning, Java, encoding, & reproducible archive conventions |
com.cleanroommc.conventions.license |
Requires LICENSE to match the selected license conventions |
com.cleanroommc.conventions.style |
Code formatting & Checkstyle conventions |
com.cleanroommc.conventions.testing |
JUnit, AssertJ & Mockito conventions |
com.cleanroommc.conventions.benchmarking |
JMH benchmarking suite in an isolated benchmark source set |
com.cleanroommc.conventions.publishing |
Maven & Gradle Plugin Portal publishing conventions |
com.cleanroommc.conventions.mod |
CurseForge & Modrinth publishing conventions |
pluginManagement {
repositories {
maven {
url = 'https://maven.cleanroommc.com'
}
gradlePluginPortal()
}
}
plugins {
id 'com.cleanroommc.conventions.settings' version '1.0.0'
}plugins {
id 'java'
id 'com.cleanroommc.conventions'
}The aggregate plugin also applies Token Envoy 1.1.0. Configure its tokenEnvoy extension to replace @{NAME} tokens in compiled classes and resources without rewriting source files. The Token Envoy version is pinned by each Conventions release and cannot be overridden per project. Its @{NAME} syntax is unrelated to the @YEAR@ and @LICENSE_HEADER@ placeholders used by license templating.
A repositories { } block in build.gradle is allowed and only appends. It cannot replace Maven Central, the Plugin Portal, or Cleanroom Maven.
These values are read before the project DSL, so they remain Gradle properties. The
license mode in particular is resolved while plugins apply, before the conventions { }
extension block below evaluates.
| Property | Default | Behaviour |
|---|---|---|
conventions.license |
visible |
free (MIT), open (LGPLv3), or visible |
conventions.javaMajor |
25 |
Java toolchain language version |
conventions.provisionJava |
false |
Settings plugin applies Foojay toolchain resolver |
conventions.modPublishing |
false |
Applies the mod conventions |
conventions.benchmarking |
false |
Applies the benchmarking conventions |
conventions.checkstyleVersion |
14.0.0 |
Checkstyle version |
Project-level dependency, publishing and copyright values belong to the managed conventions extension:
conventions {
beginFrom = 2021
repositoryUrl = 'https://github.com/CleanroomMC/example'
junitVersion = '6.1.3'
mockitoVersion = '5.23.0'
assertjVersion = '3.27.7'
jmhVersion = '1.37'
jspecifyVersion = '1.0.0'
}The existing conventions.repoUrl, conventions.junitVersion, conventions.mockitoVersion, conventions.assertjVersion, conventions.jmhVersion and conventions.jspecifyVersion Gradle properties remain supported as defaults for compatibility and CI overrides. An extension value takes precedence.
Important
Cleanroom Versioning is applied by the base conventions and refuses to apply without version
and versioning.stage (one of alpha, beta, rc, release).
Both are required in every consuming project.
Gradle reads checkstyle.xml, formatj.toml, cliff.toml, the selected LICENSE, and its HEADER from the plugin jar.
Git, editors and git-cliff still need files on disk.
checkLicense also reads LICENSE from the project directory or a parent directory, and every Java file has to start with HEADER.
extractConventions writes them into the root project directory. In a multi-project build every project applying the
base or style conventions registers its own copy of the task, all writing the same root directory:
LICENSE,HEADERcheckstyle.xml,formatj.toml,cliff.toml.editorconfig,.gitattributes.gitignore(replaces the# >>> cleanroom-conventionsregion, keeps anything outside it, and fails if the region is opened but never closed)
It is a manual task. Hook it from a project-specific setup task if you want it on a known name:
tasks.register('setup') {
dependsOn 'extractConventions'
}Applied from settings.gradle.
- Maven Central, the Gradle Plugin Portal, and Cleanroom Maven are injected before the project buildscript runs, so a later
repositories { }only appends.com.cleanroommc,top.outlands,zone.rong,net.minecraftforge,de.oceanlabs.mcpresolve from Cleanroom Maven.
- Foojay toolchain resolver, only when
conventions.provisionJava = true.
- Applies
com.cleanroommc.versioninggradle plugin.- Configures projects to follow Cleanroom's Versioning Conventions.
- Default
groupiscom.cleanroommcwhen the project has not set one. - Force UTF-8 encoding on ALL
JavaCompile,JavadocandTesttasks. - Mutes Javadoc's
missingwarnings, everything else in-Xdoclintstays on. - Java toolchain from
conventions.javaMajor. org.jspecify:jspecifyascompileOnlyon every source set.- IDEA module downloads sources and Javadoc.
- Jar manifest
Implementation-*andSpecification-*match the POM identity (name, version, CleanroomMC). - Verifiable rebuilding of artifacts
- Registers
extractConventions. It is not attached tobuild,checkorassemble.
Set one license mode in gradle.properties:
conventions.license value |
License | SPDX identifier |
|---|---|---|
free |
MIT License | MIT |
open |
GNU Lesser General Public License version 3 | LGPL-3.0-only |
visible |
CleanroomMC License Version 1.0 | Custom |
visible is the default. The selected mode controls checkLicense, extractConventions, the Java header required by Checkstyle, and Maven POM license metadata. The license conventions apply lifecycle-base, so checkLicense is attached to check even without the java plugin, and it accepts a matching LICENSE in the project directory or a parent directory.
conventions.beginFrom optionally sets the first copyright year. The generated notice uses only the current year when it is unset and no existing notice is present. With an earlier starting year, it uses StartingYear-CurrentYear, for example 2021-2026. When the year changes, extractConventions reads the starting year already stored in HEADER or LICENSE, preserves it, and advances the ending year. An explicit beginFrom value takes precedence. In open mode only HEADER carries the year: the LGPL license body is the unmodified FSF text and holds no project copyright line. Year preservation only matches CleanroomMC contributors notices; renaming the holder starts a new range from the current year.
- Applies ClearSkies, which expands star imports. No configuration.
- Applies FormatJ with
formatj.toml. - Applies Checkstyle with
checkstyle.xml.
The three run in a fixed order, since each one judges what the previous one wrote:
- ClearSkies > FormatJ > Checkstyle
Checkstyle requires the selected license header from HEADER as a Java block comment at the top of every .java file.
It is matched line by line as a regular expression, with the copyright year left as a pattern, so a new year never
invalidates the header already written into every source file.
The checkstyle.xml on disk holds an @LICENSE_HEADER@ placeholder rather than a
usable header. The plugin generates the resolved configuration at
build/conventions/checkstyle.xml; point IDE Checkstyle integrations at the generated file.
Checkstyle warns when an imported Nullable, NonNull, Nonnull, NotNull or CheckForNull annotation does not come from org.jspecify.annotations. The advisory stays at import level so legacy or generated fully-qualified references do not block a build.
Note
FormatJ ships an IntelliJ plugin that reads formatj.toml.
Run extractConventions (or copy this file to the project root) and use the plugin to perform native formatting.
org.junit:junit-bom,junit-jupiterandjunit-platform-launcher.mockito-coreandmockito-junit-jupiter.org.assertj:assertj-bom,assertj-coreandassertj-guava.useJUnitPlatform()on everyTest.- Test logging prints passed, skipped and failed, with full exception traces.
Disabled by default. Enable it through the aggregate plugin in gradle.properties:
conventions.benchmarking = trueYou can also apply com.cleanroommc.conventions.benchmarking directly. It creates an isolated benchmark source set rooted at src/benchmark/java and src/benchmark/resources, with OpenJDK JMH on its implementation and annotation processor classpaths. Its compile and runtime classpaths include main output and dependencies. It does not inherit from test or run as part of test, check or build.
Put JMH benchmarks under src/benchmark/java and add any benchmark-only libraries to benchmarkImplementation:
dependencies {
benchmarkImplementation 'org.example:benchmark-fixtures:1.0.0'
}Run all benchmarks:
./gradlew benchmarkPass standard JMH arguments through the JavaExec task. For example:
./gradlew benchmark --args='MyBenchmark -wi 3 -i 5 -f 2'-
Adds the
CleanroomMaven repository (https://maven.cleanroommc.com)- Authenticate with
CleanroomUsernameandCleanroomPassword.
- Authenticate with
-
Adds a
sourcesjar and ajavadocjar. -
Fills in POM defaults on every Maven publication:
- Name
- Description
- Url
- CleanroomMC organization
- Selected license name and URL
scmconnections.- Repository URL comes from
conventions.repositoryUrl, then the git upstream remote gradlePlugin.vcsUrlfor plugin projects.
- Repository URL comes from
-
Creates a
mavenpublication from thejavacomponent (if there is no existingmavenpublication)- Unless the project applies
java-gradle-plugin(which brings its ownpluginMavenpublication)
- Unless the project applies
-
For
java-gradle-pluginprojects: appliescom.gradle.plugin-publish. -
Signs every Maven publication when both
signingKeyandsigningPasswordare set.- If either property is missing, signing is left off and publish tasks still run.
-
Nothing here writes
cliff.toml. Only thegit-cliffCLI reads it, so the release workflow fetches it from this repository at the ref the workflow was called at, unless the project ships its own.
Disabled by default. Enable it in the project's gradle.properties:
conventions.modPublishing = trueThis exposes the mod-publish-plugin's publishMods extension. Add either or both distributions:
Note
The following properties have been applied:
- Minecraft Version: "1.12.2"
- Mod Loader: "forge" ("cleanroom" if/when distributions support it)
- File: output of the "jar" task
- Max Retries: 5
- Version:
project.version - Version Type: (Alpha/Beta/Stable) applied via Cleanroom's Versioning module
- CurseForge Access Token:
CURSEFORGE_TOKENenvironment variable - Modrinth Access Token:
MODRINTH_TOKENenvironment variable
Tip
Any value can be overridden through the upstream DSL. A distribution is only configured when it is named here. Check out the plugin's wiki
conventions {
mods {
curseforge = '123456'
modrinth = 'abcdef'
// curseforge { }
// modrinth { }
// To configure the upstream DSLs
}
}Reusable workflows live in this repository. Pin the @ ref to a tag (or a commit), which can be better than pinning to @master which tracks whatever is latest.
This repository's own wrappers are .github/workflows/ci.yml and .github/workflows/publish.yml. Other CleanroomMC projects should call the reusable files below.
.github/workflows/build.yml compiles, tests and uploads **/build/libs.
name: CI
on:
push:
pull_request:
types: [opened, synchronize, reopened, ready_for_review, review_requested]
workflow_dispatch:
permissions:
contents: read
jobs:
build:
uses: CleanroomMC/Conventions/.github/workflows/build.yml@master
# with:
# working-directory: gradle-plugin
# java-version: '25'| Input | Default | Purpose |
|---|---|---|
working-directory |
. |
Directory that contains gradlew, or a unique wrapper two levels down |
artifact-path |
**/build/libs |
Paths uploaded after a successful build |
if-no-files-found |
warn |
warn, error or ignore when nothing matches |
java-version |
25 |
Temurin JDK used to launch Gradle |
timeout-minutes |
15 |
Job timeout |
cache-provider |
enhanced |
basic (MIT) or enhanced (Gradle Terms of Use) |
build-scan-publish |
true |
Publish build scans to scans.gradle.com |
The workflow runs ./gradlew build -Pversioning.run=${{ github.run_number }}. Draft PRs are skipped until they are marked ready for review.
.github/workflows/release.yml builds with -Pversioning.publish, generates release notes and CHANGELOG.md through git-cliff, then optionally publishes.
name: Publish
on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+'
workflow_dispatch:
permissions:
contents: write
issues: read
pull-requests: read
jobs:
release:
uses: CleanroomMC/Conventions/.github/workflows/release.yml@master
with:
publish-maven: true
publish-plugin-portal: false
publish-mods: false
secrets: inheritA library typically enables publish-maven. A Gradle plugin also sets publish-plugin-portal: true. A mod sets conventions.modPublishing = true in Gradle and publish-mods: true here.
| Input | Default | Purpose |
|---|---|---|
working-directory |
. |
Directory that contains gradlew |
artifact-path |
**/build/libs/*.jar |
Jars attached to the GitHub Release |
cliff-config |
cliff.toml |
Local override. If missing, the workflow fetches this repo's cliff.toml |
java-version |
25 |
Temurin JDK used to launch Gradle |
timeout-minutes |
30 |
Job timeout |
cache-provider |
basic |
Gradle User Home cache |
publish-maven |
true |
publishAllPublicationsToCleanroomRepository |
publish-plugin-portal |
false |
publishPlugins |
publish-mods |
false |
publishMods (CurseForge / Modrinth) |
| Secret | Used when |
|---|---|
MAVEN_NAME |
publish-maven |
MAVEN_PASSWORD |
publish-maven |
GRADLE_PUBLISH_KEY |
publish-plugin-portal |
GRADLE_PUBLISH_SECRET |
publish-plugin-portal |
SIGNING_KEY |
publish-plugin-portal |
SIGNING_PASSWORD |
publish-plugin-portal |
CURSEFORGE_TOKEN |
publish-mods |
MODRINTH_TOKEN |
publish-mods |
publish-mods needs at least one of the two store tokens. A tag always creates a GitHub Release with CHANGELOG.md and the matched jars, even when every publish input is false.
git-cliff uses the first of: the path in cliff-config, that file at the repository root, then cliff.toml from this Conventions ref.