Publishing

Publishing

build-logic handles two publish channels: Maven artifacts to the self-hosted Reposilite repository, and mod releases to Modrinth. Both are driven from the matrix so a release fans out across every target without listing versions in a workflow.

Maven Coordinates

gradle/publishing.properties is the single source of truth for the group and target repository:

group=dev.ua.theroerrepo.url=https://maven.theroer.dev/releases

Per-target versioning

Each target is its own Maven version, using a Fabric-style suffix <base>+<minecraft> (for example 1.22.0+1.21.10). Every target's module metadata (Java level, transitive deps) is therefore isolated and never collides with another target's. Consumers still declare the bare base version; the consumer plugins mirror the suffix from their resolved target.

The main jar carries no Minecraft classifier (the branch is already in the version, same as fabric-api). Classifiers are reserved for genuinely different jars: all (shaded fat jar), dev, and sources.

Publishing To Reposilite

Publish tasks are grouped by category so a release can target the right subset:

TaskWhat it publishes
publishDefaultMatrixEvery publishable module on the default target.
publishCommonMatrixCommon (platform-neutral) modules — use with -Ptarget=.
publishFabricMatrixFabric-matrix modules — use with -Ptarget=.

Point publishing at the repo and supply credentials via environment variables (PUBLISH_USER / PUBLISH_TOKEN), read by the publish plugin:

bash
PUBLISH_USER=... PUBLISH_TOKEN=... \  ./gradlew publishCommonMatrix \    -Ptarget=mc262 \    -Ppublish_repo=https://maven.theroer.dev/releases

The CI publish job reads printPublishMatrix and runs one unit per target in parallel, each writing to a disjoint artifact path — so no assembly step is needed. The build-logic plugins themselves are published to the same repo, so external consumers resolve them alongside the library.

Publishing To Modrinth

A Modrinth release is declared in magicMatrix { modrinth { } }. It is opt-in: without a projectId there is nothing to publish.

kotlin
magicMatrix {    modrinth {        projectId = "AbCdEf12"        channel = "release"        featured = false        // Explicit artifacts are optional — see "Auto-derived artifacts" below.        artifact("bukkit") {            file = "bukkit-bundle/build/libs/mymod-bukkit-bundle-${version}.jar"            platform = "bukkit"           // derives loaders; or set loaders explicitly            gameVersions = listOf("1.21.10")        }        artifact("fabric") {            file = "fabric-bundle/build/libs/mymod-fabric-bundle-${version}.jar"            loaders = listOf("fabric")            gameVersions = listOf("1.21.10")        }    }}

Loaders per platform

Set platform = "..." on an artifact and the Modrinth loader list is derived for you (leave loaders empty). The built-in mapping is:

platformModrinth loaders
bukkitpaper, purpur, spigot, bukkit, folia
bungeebungeecord, waterfall
velocityvelocity
fabricfabric
neoforgeneoforge

Set loaders = listOf(...) explicitly when you need to override the mapping. The bukkit bundle claims the folia loader — that claim is backed by a real Folia smoke run (see Dev Server & Smoke), even though there is no separate folia-bundle jar.

Auto-derived artifacts

You usually don't list artifact(...) blocks at all. When the modrinth { } block declares none, the release tasks derive one artifact per (platform × smoke game-version) straight from the smoke matrix: the platform picks the loader set, the target picks the jar name (magicutils-<platform>-bundle-<base>+<mc>.jar), and the smoke entry's versions expand into the Modrinth game_versions. This keeps the published release and the smoke gate in lockstep — you never hand-maintain a parallel artifact list. Smoke-only platforms that ship no bundle of their own (such as folia) are skipped by the generator. Declare explicit artifact(...) entries only to override the derived set.

Changelog

Every uploaded version carries a Markdown changelog. Its resolution order:

  1. -PmodrinthChangelogFile=path — read the changelog from that file.
  2. An explicit changelog = "..." string in the modrinth { } block.
  3. Otherwise it is generated from git history: conventional-commit subjects since the previous tag are grouped into sections (feat → Added, fix → Fixed, perf → Improved, refactor → Changed, the rest → Maintenance). Merge, fixup!/squash!, and version-bump commits are dropped. Override the diff base with -PmodrinthPreviousTag=<tag>.

Then publish one version per declared or derived artifact:

bash
MODRINTH_TOKEN=... ./gradlew publishToModrinth -Pversion=1.22.0

Behaviour:

  • The token comes only from MODRINTH_TOKEN, never from the build script.
  • The task is idempotent: a version number that already exists on the project is skipped, so re-running a release is safe.
  • -Pversion sets the base version; each artifact's Modrinth version number is derived from it.

Release Fan-Out

printReleaseMatrix emits one JSON row per (platform × smoke entry) with the target, artifact classifier, and game-version ranges. A consumer's release workflow turns each row into a build command and a Modrinth upload, so the same matrix that builds and smoke-tests also drives the release.