From 77e963bfb16edd1fac862ee9b7767d04c1ac88aa Mon Sep 17 00:00:00 2001 From: Rongmario Date: Sun, 2 Aug 2026 00:17:31 +0100 Subject: [PATCH] ci: wire up Modrinth and CurseForge publishing Fills in the project IDs so publishMods stops skipping both platforms, verified end to end with a dry run on Fabric, Forge and NeoForge. Also two things that would have bitten the first real release: The tag is now checked against mod_version before anything uploads. A jar carries the version it was built with, so tagging v7.0.1 over a stale mod_version would have published 80 files as 7.0.0, and a file on CurseForge can't be replaced afterwards. Uploads run four at a time rather than one. Every job decompiles its own Minecraft, so 80 serial targets would have taken most of a day. Co-Authored-By: Claude Opus 5 --- .github/workflows/publish.yml | 20 +++++++++++++++++++- README.md | 25 ++++++++++++++++--------- gradle.properties | 9 +++++---- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 2a63f65..dcd05c5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -21,6 +21,20 @@ jobs: matrix: ${{ steps.read.outputs.matrix }} steps: - uses: actions/checkout@v4 + + # A jar carries the version from gradle.properties, not the tag. Without + # this, tagging v7.0.1 while mod_version still says 7.0.0 publishes 80 + # files under the old version, and CurseForge files can't be replaced. + - name: Check the tag matches mod_version + if: github.event_name == 'release' + run: | + declared=$(sed -n 's/^mod_version[[:space:]]*=[[:space:]]*//p' gradle.properties | tr -d '[:space:]') + tagged=${GITHUB_REF_NAME#v} + if [ "$declared" != "$tagged" ]; then + echo "::error::release ${GITHUB_REF_NAME} would publish mod_version ${declared}; bump gradle.properties or retag" + exit 1 + fi + - id: read run: echo "matrix=$(jq -c . .github/targets.json)" >> "$GITHUB_OUTPUT" @@ -31,7 +45,11 @@ jobs: strategy: # Uploads are independent; one bad target shouldn't cancel the rest. fail-fast: false - max-parallel: 1 + # Each job decompiles its own Minecraft, so a target costs minutes, and + # there are 80 of them - serial uploads would run most of a day. Four at + # a time matches the build workflow's headroom against Maven Central and + # stays well inside both platforms' upload rate limits. + max-parallel: 4 matrix: target: ${{ fromJson(needs.targets.outputs.matrix) }} steps: diff --git a/README.md b/README.md index e24f5d4..505a9c6 100644 --- a/README.md +++ b/README.md @@ -154,15 +154,22 @@ Then build it. If an API moved, the compiler will say so, and the fix is another Releases go to Modrinth and CurseForge from `.github/workflows/publish.yml`, driven by [mod-publish-plugin](https://github.com/modmuss50/mod-publish-plugin). -One-time setup: - -1. Put the project IDs in `gradle.properties` (`modrinth_id`, `curseforge_id`). - An empty ID makes that platform get skipped rather than fail. -2. Add `MODRINTH_TOKEN` and `CURSEFORGE_TOKEN` as repository secrets. - -Publishing a GitHub release then uploads every target, using the release body as -the changelog. To rehearse it, run the workflow manually with `dry_run` ticked — -that builds and validates the uploads without sending anything. +Both are wired up: the project IDs are in `gradle.properties` (`modrinth_id`, +`curseforge_id`) and the `MODRINTH_TOKEN` and `CURSEFORGE_TOKEN` secrets are set +on the repository. Clearing an ID makes that platform get skipped rather than +fail. + +To cut a release: + +1. Bump `mod_version` in `gradle.properties` and push it. +2. Run the workflow manually with `dry_run` ticked. That builds and validates + every upload without sending anything. +3. Tag and publish a GitHub release as `v`. The release body + becomes the changelog on both platforms. + +The tag has to match `mod_version`, and the workflow stops before uploading +anything if it doesn't — a jar carries the version it was built with, and a +file published to CurseForge can't be replaced afterwards. ## License diff --git a/gradle.properties b/gradle.properties index b95b386..9362d1e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -14,9 +14,10 @@ mod_homepage = https://www.curseforge.com/minecraft/mc-mods/cleancut mod_sources = https://github.com/Rongmario/CleanCut mod_issues = https://github.com/Rongmario/CleanCut/issues -# Publishing targets. Fill these in before the first release; an empty slot -# makes `publishMods` skip that platform instead of failing. -modrinth_id = -curseforge_id = +# Publishing targets. An empty slot makes `publishMods` skip that platform +# instead of failing. Tokens come from the MODRINTH_TOKEN and CURSEFORGE_TOKEN +# environment variables, set as repository secrets for the Publish workflow. +modrinth_id = CCNUnbXG +curseforge_id = 392805 fabric_loader_version = 0.16.14