diff --git a/.github/workflows/module-ci.yml b/.github/workflows/module-ci.yml index 24aa31a..d5d3298 100644 --- a/.github/workflows/module-ci.yml +++ b/.github/workflows/module-ci.yml @@ -78,3 +78,21 @@ jobs: steps: - uses: actions/checkout@v7 - uses: fsfe/reuse-action@v6 + + # A Module whose textures scripts/build-textures.py builds, such as those made from third-party + # art (NOTICE), commits what the script writes; --check fails when one differs. Without the + # script there is nothing to check. + textures: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - id: script + run: | + if [ -f scripts/build-textures.py ]; then echo "found=true" >> "$GITHUB_OUTPUT"; fi + + - if: steps.script.outputs.found == 'true' + run: pip install pillow + + - if: steps.script.outputs.found == 'true' + run: python3 scripts/build-textures.py --check diff --git a/CHANGELOG.md b/CHANGELOG.md index efd4389..002f632 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Written for Consumers: what a mod building against Example Module can use, or wi ## Unreleased +- `module-ci.yml`, from the next `plugin-v` tag: a `textures` job runs `python3 scripts/build-textures.py --check`, with Pillow, when the Module has that script, and does nothing otherwise, so a Module drops its own texture job when it moves to that tag (#27). The template ships `NOTICE` with the preamble every Module's shares and no entries, and a commented `REUSE.toml` override stanza for third-party art such as Malcolm Riley's unused textures, which needs `LICENSES/CC-BY-4.0.txt` once uncommented. - `libworks-plugin` 0.4.1: `upload` records each upload a site takes beside the jar, in `-.uploads.json` in `~/.m2` with the site's id for it and when, and refuses a site already recorded before contacting it, so a `-Psite=curseforge` retry no longer uploads a version again while its first file waits in CurseForge's review, which the website's listing doesn't show (#22). The record only knows the machine that uploaded. A site's entry is removed by hand only when that site rejected the file or never showed it, and `-PdryRun` shows what the record holds (`docs/agents/releases.md`). - `libworks-plugin` 0.4.0, breaking: the plugin declares the Module's `~/.m2` repository, `exclusiveContent` for the whole `io.github.5thlayer` group, so every 5thlayer module resolves from `~/.m2` alone and `~/.m2` serves nothing else (`docs/plugin.md`, #18). A Module that takes it deletes its own `~/.m2` repository, `mavenLocal { content { … } }` or `exclusiveContent { forRepository { mavenLocal() } … }`: the plugin refuses either, naming it. The template's `build.gradle` has none. - The template's game-test code never ships (libworks ADR 0011, #17): `ExampleLibGameTests`, `CodeGameTest`, the test classes and `structure/gametest/platform.nbt` are in the `gametest` source set, and the jar holds none of them. `ExampleLibGameTests` is an `@EventBusSubscriber` that registers from `FMLConstructModEvent`, so `ExampleLib` names no game-test code, and nothing gates it: it registers in every one of the Module's own runs. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..7b20067 --- /dev/null +++ b/NOTICE @@ -0,0 +1,7 @@ +Third-party material committed to this repository +================================================== + +Each entry names the files, their author, where they came from and the licence they are under. A +file listed here is NOT covered by the licence this repository states for its own work; its own +licence governs it. Which licence covers which file is REUSE.toml's to say; this list is the +credits for what was copied in. diff --git a/README.md b/README.md index 9101e8a..195e923 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ libworks is becoming a dependency (ADR-0128, #8). It carries `runtime/`, a small - A GameTest harness: `runGameTestServer` runs the Module's game tests headless and fails if it ran none. `buildGametestStructures` writes the stone platform they stand on. - JUnit tests on a plain JVM, with no Minecraft. - `sh ./gradlew release -PreleaseVersion=`, which releases a version to `~/.m2` and tags it; a published version never changes. The `skillworks:quicklaunch` skill opens the dev client into the most recent save in `run/saves`, one client per checkout. -- CI on every push, through libworks' `module-ci.yml` at the plugin's tag: the build, the JUnit tests, the game tests, and a REUSE lint. It never publishes. -- MIT under REUSE, `CLAUDE.md`, a `GLOSSARY.md` stub, `docs/agents/`, conventional commits. +- CI on every push, through libworks' `module-ci.yml` at the plugin's tag: the build, the JUnit tests, the game tests, a REUSE lint, and `scripts/build-textures.py --check` when the Module has that script. It never publishes. +- MIT under REUSE, with a `NOTICE` for the credits of third-party material and a commented `REUSE.toml` stanza for its licence, `CLAUDE.md`, a `GLOSSARY.md` stub, `docs/agents/`, conventional commits. - The engineering skills from [mattpocock/skills](https://github.com/mattpocock/skills), a submodule at `vendor/mattpocock-skills` pinned to a release and linked into `.claude/skills/`. - ADR 0001, the versioning rule every Module inherits: below 1.0 an addition bumps the patch. @@ -23,7 +23,7 @@ libworks is becoming a dependency (ADR-0128, #8). It carries `runtime/`, a small ## Adopting it in an existing Module -A Module that predates the template is not regenerated. Diff it against the template and take the template's version of each shared piece: `settings.gradle` and `build.gradle` applying the plugin in place of its own build logic and `scripts/`, the game test harness (`CodeGameTest` and the `*GameTests` registrar), `REUSE.toml`, `docs/agents/`, `.github/workflows/`, and the skills submodule with its links. Its own versioning ADR becomes a pointer to libworks' ADR 0001. +A Module that predates the template is not regenerated. Diff it against the template and take the template's version of each shared piece: `settings.gradle` and `build.gradle` applying the plugin in place of its own build logic and `scripts/`, the game test harness (`CodeGameTest` and the `*GameTests` registrar), `REUSE.toml` and `NOTICE`'s preamble, `docs/agents/`, `.github/workflows/`, and the skills submodule with its links. Its own versioning ADR becomes a pointer to libworks' ADR 0001. ## Changing the template diff --git a/REUSE.toml b/REUSE.toml index 95a3cb6..bb46aad 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -25,11 +25,34 @@ path = [ "**/src/gametest/resources/**/*.nbt", "**/src/main/resources/**/*.nbt", "LICENSE", + "NOTICE", ".github/**", ] SPDX-FileCopyrightText = "2026 5thlayer" SPDX-License-Identifier = "MIT" +# Third-party art, such as Malcolm Riley's unused textures +# (https://github.com/malcolmriley/unused-textures), takes an override stanza of its own and an +# entry in NOTICE that credits it. Unmodified copies, those a texture is built from under data/art/ +# included, are the author's; a texture scripts/build-textures.py builds from them is both. Module +# CI runs that script's --check when it exists. Uncommenting a stanza needs the licence's text in +# LICENSES/ (reuse download CC-BY-4.0), which reuse lint refuses while no file uses it. +# +# [[annotations]] +# path = [ +# "data/art/*.png", +# "src/main/resources/assets/examplelib/textures/block/plating/*.png", +# ] +# precedence = "override" +# SPDX-FileCopyrightText = "Malcolm Riley" +# SPDX-License-Identifier = "CC-BY-4.0" +# +# [[annotations]] +# path = ["src/main/resources/assets/examplelib/textures/block/machine/front.png"] +# precedence = "override" +# SPDX-FileCopyrightText = ["Malcolm Riley", "2026 5thlayer"] +# SPDX-License-Identifier = "CC-BY-4.0" + # The Gradle wrapper is Gradle's. [[annotations]] path = ["gradlew", "gradlew.bat", "gradle/wrapper/**"] diff --git a/docs/plugin.md b/docs/plugin.md index d2c7ef6..744d193 100644 --- a/docs/plugin.md +++ b/docs/plugin.md @@ -11,6 +11,8 @@ plugins { id 'io.github.5thlayer.libworks.module' version '0.1.2' } Its CI is `.github/workflows/module-ci.yml`, called at the same tag: `uses: 5thlayer/libworks/.github/workflows/module-ci.yml@plugin-v`. `fill-template.sh` writes both at the version `plugin/gradle.properties` holds. `module-ci.yml` reads the version from that `uses:` ref and fails when `build.gradle` applies another, so the two move together. +Beside the build and `reuse lint`, it runs `python3 scripts/build-textures.py --check`, with Pillow, when the Module has that script, and skips it otherwise. The script is the Module's own: it builds the textures it commits, typically from third-party art kept unmodified under `data/art/`, and `--check` fails when a committed one differs from what it builds. The art is credited in `NOTICE` and licensed by an override stanza in `REUSE.toml`, whose commented example the template ships. + An upstream is declared as `upstream('') { range = '[0.5.4,0.6)' }`, with `tagPrefix` when its tags are not `-v`. The range's lower bound is the version the Module prefers and the one CI builds from its tag, so it must be a released version. An upstream with `optional = true` goes on `localRuntime`, a configuration the plugin creates (or takes over, when the Module declares one) and `runtimeClasspath` extends: the dev runs load it, and the published jar, pom and module metadata do not depend on it. `libworksUpstreams` still lists it, so CI builds its tag, and `-PsiblingBuilds` still checks its range. diff --git a/scripts/tests/test_fill_template.py b/scripts/tests/test_fill_template.py index 1b224ad..6ecc8b0 100644 --- a/scripts/tests/test_fill_template.py +++ b/scripts/tests/test_fill_template.py @@ -111,6 +111,12 @@ def test_reuse_names_no_path_the_fill_removes(self): self.assertNotIn("plugin/", reuse) self.assertNotIn('"scripts/', reuse) + def test_the_module_keeps_notice_with_no_entries_and_reuse_covers_it(self): + notice = self.text("NOTICE") + self.assertTrue(notice.startswith("Third-party material committed to this repository\n"), notice) + self.assertNotIn("Files:", notice) + self.assertRegex(self.text("REUSE.toml"), r'(?m)^ "NOTICE",$') + def test_the_changelog_keeps_its_header_and_an_empty_unreleased(self): changelog = self.text("CHANGELOG.md") self.assertTrue(changelog.startswith("# Changelog\n"), changelog)