Releasing¶
Versions, branches and releases. How the project is built: Development.
In short¶
- Work on
rc(or on branches merged intorc), noting changes under## [Unreleased]inCHANGELOG.md. - When ready, on
rc, bump the version: gradle.properties:mod_version=0.5.0CHANGELOG.md: under the empty## [Unreleased], add## [0.5.0] - 2026-10-15with what changed (square brackets and this exact format: the release notes are taken from it).- Optionally run the Game tests workflow on
rc(Actions tab → Game tests → Run workflow, Use workflow from:rc): CI doesn't play them. - Open a pull request from
rcintomain, wait for CI (its release check confirms the new version), merge. - Done. GitHub creates
release/0.5.0, the tagv0.5.0-fabload0.19.5+api0.162.0-mc26.3and the release (a pre-release, Beta on Modrinth: promote it by hand) with the jar, the.mrpackand the Default Content datapack zip, then uploads the jar to Modrinth (about a minute: Actions tab, then Releases). rcis brought up to date withmainautomatically; locally, rungit pullonrc.- If the release should have MineDnD Studio installers, run the Studio installers workflow with its tag (see below).
The sections below explain each part.
Version numbers¶
MineDnD uses Semantic Versioning: MAJOR.MINOR.PATCH.
| Change | Bump | Example |
|---|---|---|
| Bug fixes only | PATCH | 0.0.1 → 0.0.2 |
| New features, new datapack formats, balance changes | MINOR | 0.0.2 → 0.1.0 |
| Changes that break existing worlds or datapacks (after 1.0.0) | MAJOR | 1.4.2 → 2.0.0 |
While the version starts with 0., anything can still change between minor versions.
The version lives in one place: mod_version in gradle.properties. It flows into the jar name
(MineDnD-<version>.jar), fabric.mod.json (shown in the mods list) and the modpack (MineDnD-<version>.mrpack).
Branches and tags¶
| Name | What it is |
|---|---|
main |
What's released. Every merge into main is a release: pull requests into it come from rc and carry a new version. |
rc |
Release candidate: where changes collect and get tested (installToModrinth, playtests) before a release. |
feature/..., fix/..., chore/... |
Short-lived work branches, merged into rc by pull request (or commit straight on rc). |
release/X.Y.Z |
Frozen copy of a released version, to fix it later without taking new features. Created automatically. |
tag vX.Y.Z-fabloadL+apiA-mcM |
The exact commit that was released, e.g. v0.4.2-fabload0.19.5+api0.162.0-mc26.3: mod version, Fabric Loader version (loader_version), Fabric API version (fabric_version without +<minecraft>) and Minecraft version (minecraft_version). Created automatically. Up to 0.4.1 tags were plain vX.Y.Z. |
Making a release¶
Work happens on rc; merging rc into main releases.
- Make changes on
rc(commits, or pull requests fromfeature/...branches intorc). CI builds every push torc. Note them under[Unreleased]inCHANGELOG.md. Try them in game withgradlew installToModrinth -Pinstance="<folder>". - When
rcis ready: onrc, setmod_versioningradle.propertiesand move the[Unreleased]notes inCHANGELOG.mdunder a new## [X.Y.Z] - YYYY-MM-DDheading. - Open a pull request from
rcintomain. Its release-check job fails if the version is already released or the changelog has no section for it, so every merge intomainreally is a release. - Merge it. On every push to
main, the Release workflow (.github/workflows/release.yml) checks whetherX.Y.Zalready has a tag: - No: it creates
release/X.Y.Zand the tag (e.g.v0.4.2-fabload0.19.5+api0.162.0-mc26.3, also the release title), builds, and publishes a GitHub Release withMineDnD-X.Y.Z.jar, the sources jar,MineDnD-X.Y.Z.mrpackandMineDnD-Default-Content-X.Y.Z.zip(the datapack), using the version'sCHANGELOG.mdsection as notes. Every release is a pre-release on GitHub and Beta on Modrinth: promote it by hand when it's ready (Modrinth: the version's Edit → release channel; GitHub: Edit release). Then it uploads the jar to Modrinth (see below). - Yes: nothing is released (the workflow says so in its summary).
rcis brought up to date withmainautomatically: the Release workflow's last step mergesmainintorc(usually a fast-forward). Only after a conflict, which can happen ifrcgot new commits in the meantime, does it warn and ask you to rungit checkout rc && git merge origin/main && git pushyourself. Locally,git pullonrcgets the update.
Watch it in the repository's Actions tab; the release appears on the Releases page and on Modrinth.
Studio installers¶
The MineDnD Studio installers (Windows, macOS, Linux) are built only by hand, so a release doesn't wait for three
runners and pull requests don't pay for them. Actions tab → Studio installers → Run workflow
(.github/workflows/studio.yml):
- Release tag filled in (e.g.
v0.9.0-fabload0.19.5+api0.162.0-mc26.3): builds that tag on one runner per OS and adds the installers to its GitHub release. The release must exist already; running it again replaces the files. - Release tag empty: builds the branch chosen in Use workflow from and keeps the installers as the run's artifacts for 14 days, e.g. to check that packaging still works after changing Studio's dependencies.
From a terminal: gh workflow run studio.yml -f release_tag=<tag> (or without -f to only build).
Documentation site¶
The docs are also a website, https://minednd.pages.dev/ (MkDocs Material, English and Italian, with search), on
Cloudflare Pages: free, and the repository can stay private. The Docs workflow (.github/workflows/docs.yml)
builds and publishes it on every push to main that changes docs/, so the site always describes the released
version; it can also be run by hand from the Actions tab.
Set up once:
- Account ID: on dash.cloudflare.com, open Workers & Pages (or the account's home): the Account ID is in the
right-hand column (also in the address bar:
dash.cloudflare.com/<account id>/…). Store it as the secretCLOUDFLARE_ACCOUNT_ID:gh secret set CLOUDFLARE_ACCOUNT_ID(paste it when asked). - API token: avatar → Profile → API Tokens → Create Token → Custom token. Permissions: Account →
Cloudflare Pages → Edit; Account resources: your account. Store it as the secret
CLOUDFLARE_API_TOKEN:gh secret set CLOUDFLARE_API_TOKEN. - Run the Docs workflow (Actions tab → Docs → Run workflow, on
main). The first run creates the Pages projectminednd(another name: the repository variableCLOUDFLARE_PAGES_PROJECT, andsite_urlinmkdocs.yml).
Until the secrets are set, the workflow builds the site and only warns. CI builds the site on every pull request (the
docs job), so a broken link or anchor in either language fails there. How the site is made:
Development.
Modrinth uploads¶
The Release workflow runs .github/scripts/modrinth-upload.sh after the GitHub release. It uploads MineDnD-X.Y.Z.jar
as version X.Y.Z: Fabric, the Minecraft version from gradle.properties, Fabric API as a required dependency, the
release notes as changelog, always Beta (promoted by hand). A version already on Modrinth is skipped, so re-running is safe.
Set up once (until then the step only prints "skipped"):
- On modrinth.com: avatar → Settings → Personal access tokens → create one with the Create versions scope (and Read projects, to find the project while it's a draft or private).
- Store it in the repository as the secret
MODRINTH_TOKEN:gh secret set MODRINTH_TOKEN(paste it when asked), or Settings → Secrets and variables → Actions → New repository secret. - Store the project's slug (e.g.
minednd) as the variableMODRINTH_PROJECT:gh variable set MODRINTH_PROJECT --body minednd.
CI also runs two checks on every pull request:
- a dry run of the upload (MODRINTH_DRY_RUN=1), which checks the data without contacting Modrinth;
- an access check (MODRINTH_CHECK=1), which reads the project with the token and lists its versions. It shows up in the run as "Modrinth project OK: …", or as an error when the slug or token is wrong.
Fixing an old release¶
- Branch from
release/X.Y.Z, fix, bump PATCH (X.Y.Z+1), add a changelog entry, open a pull request intorelease/X.Y.Z. - After merging, tag the release branch by hand:
git tag -a "$(bash .github/scripts/release-tag.sh)" -m "MineDnD X.Y.(Z+1)"and push the tag. Pushing a release tag also publishes a release (the tag must be the onegradle.propertiesgives). - Bring the fix to
maintoo (another pull request or a cherry-pick).
Reviews and branch protection¶
.github/CODEOWNERS makes the owner the required reviewer of every file, and CI (.github/workflows/ci.yml) builds the mod
and runs the editor tests on every pull request.
The rules that enforce this (pull request required, code owner approval, CI must pass, no force pushes or deletions
on main and release/*; release tags can't be moved or deleted) are prepared in .github/rulesets/.
GitHub only enforces rulesets on private repositories with a paid plan (GitHub Pro or Team); on a public repository
they're free. To apply them, once that's the case:
gh api -X POST repos/less-hub/MineDnD/rulesets --input .github/rulesets/branches.json
gh api -X POST repos/less-hub/MineDnD/rulesets --input .github/rulesets/tags.json
The repository admin can still merge their own pull requests (GitHub doesn't let authors approve their own pull requests); everyone else's pull requests need the owner's approval.