Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Normalise line endings in the repository and check everything out as LF, on every platform.
# Without this, Git's autocrlf on Windows hands out CRLF working copies, and
# extension-descriptor-contract.json — which is compared byte for byte against the annotation
# processor's output, and that output is always LF — no longer matches.
* text=auto eol=lf

# Windows batch files only work with CRLF.
*.bat text eol=crlf
*.cmd text eol=crlf
gradlew.bat text eol=crlf

# The wrapper script is executed by sh, so it must stay LF even on Windows checkouts.
gradlew text eol=lf

*.jar binary
*.class binary
*.png binary
*.ico binary
24 changes: 23 additions & 1 deletion .github/workflows/build-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,30 @@ on: [pull_request]

jobs:
build:
uses: OneLiteFeatherNET/workflows/.github/workflows/gradle-build-pr.yml@v2.1.0
uses: OneLiteFeatherNET/workflows/.github/workflows/gradle-build-pr.yml@v2.4.0
with:
java-version: "25.0.3"
java-distribution: "temurin"
# The reusable workflow's default filter anchors sources at `src/**`, which stopped
# matching when they moved into `minestom-extensions/src/` and friends. Widened to
# `**/src/**`. `**/*.java` alone would not do: non-Java files under a module's src/ —
# notably minestom-extensions-processor's META-INF/services registration, which is what
# makes the annotation processor discoverable — would never trigger a build.
paths-filters: |
code:
- '**/*.gradle'
- '**/*.gradle.kts'
- '**/gradle.properties'
- 'gradle/**'
- 'gradlew'
- 'gradlew.bat'
- 'settings.gradle'
- 'settings.gradle.kts'
- 'buildSrc/**'
- '**/src/**'
- '**/*.java'
- '**/*.kt'
- '**/*.groovy'
- '**/*.scala'
- '.github/workflows/**'
secrets: inherit
2 changes: 1 addition & 1 deletion .github/workflows/close-invalid-prs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ on:

jobs:
close:
uses: OneLiteFeatherNET/workflows/.github/workflows/close-invalid-prs.yml@v2.1.0
uses: OneLiteFeatherNET/workflows/.github/workflows/close-invalid-prs.yml@v2.4.0
with:
protected-branch: main
2 changes: 1 addition & 1 deletion .github/workflows/release-please.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
publish:
needs: release-please
if: needs.release-please.outputs.release_created == 'true'
uses: OneLiteFeatherNET/workflows/.github/workflows/gradle-publish.yml@v2.1.0
uses: OneLiteFeatherNET/workflows/.github/workflows/gradle-publish.yml@v2.4.0
with:
java-version: "25.0.3"
java-distribution: "temurin"
Expand Down
68 changes: 63 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,67 @@ By participating in this project you agree to abide by our
- Git
- No local Gradle install required — use the bundled wrapper (`./gradlew`)

## Project layout

This is a Gradle multi-module build. The root project is an aggregator and publishes nothing itself;
all five published artifacts come from subprojects that share a single version:

| Module | Artifact | Contents |
|--------|----------|----------|
| `minestom-extensions/` | `net.onelitefeather:minestom-extensions` | The extension system: `ExtensionBootstrap`, `ExtensionManager`, `Extension`, `DiscoveredExtension`, `ExtensionClassLoader`. |
| `minestom-extensions-processor/` | `net.onelitefeather:minestom-extensions-processor` | The `@ExtensionInfo` annotation and the annotation processor that generates `extension.json`. Runtime-dependency-free by design — JDK APIs only. |
| `minestom-extensions-gradle-plugin/` | `net.onelitefeather:minestom-extensions-gradle-plugin` | Gradle plugin adding build-declared libraries to `extension.json`. Built with `java-gradle-plugin`, which supplies its own `pluginMaven` publication — the root build skips creating a second one for it. |
| `minestom-extensions-maven-plugin/` | `net.onelitefeather:minestom-extensions-maven-plugin` | The same for Maven. Its `META-INF/maven/plugin.xml` is maintained by hand, see below. |
| `minestom-extensions-bom/` | `net.onelitefeather:minestom-extensions-bom` | A `java-platform` BOM pinning the modules above. |

The packages and class names under `minestom-extensions/` are a **public contract** with downstream
consumers such as CloudNet. Do not rename or move `net.minestom.server.extensions.*` or
`net.hollowcube.minestom.extensions.ExtensionBootstrap`, even if a rename looks tidier.

`MavenDependencyResolver` is package-private on purpose — it resolves an extension's
`externalDependencies` at startup and is an implementation detail, not part of that contract. It
wires Maven Artifact Resolver through the deprecated `MavenRepositorySystemUtils.newServiceLocator()`
rather than the newer `RepositorySystemSupplier`, and that is deliberate: the supplier builds a
resolver whose descriptor reader does not interpret POMs, so transitive dependencies silently
resolve to nothing. Read the class javadoc before "modernising" it.

Shared build logic — the Java 25 toolchain, sources/javadoc jars, and the whole `maven-publish`
setup — lives once in the root `build.gradle.kts`. A module's own build file should only carry what
is genuinely specific to it (its `description` and its dependencies).

## Building and testing

```bash
./gradlew build # compile and run the full test suite
./gradlew test # run the tests only
./gradlew build # build and test every module
./gradlew test # run the tests only
./gradlew :minestom-extensions-processor:test # a single module
```

Tests run on JUnit 5 with `-Dminestom.inside-test=true` already configured by the build.
Tests run on JUnit 5. The core module's test task additionally sets `-Dminestom.inside-test=true`,
which Minestom requires; the processor's tests are plain JUnit and need no such flag. Both are
already configured by the build — you do not need to pass anything by hand.

One test spans both code modules and is worth knowing about before you touch either end.
`minestom-extensions/src/test/resources/extension-descriptor-contract.json` is the exact
`extension.json` the processor emits for a fully populated `@ExtensionInfo`. The processor module
copies that file in and asserts it still reproduces it; the core module deserializes it into the
real `DiscoveredExtension` and asserts every field arrives. This is deliberate belt-and-braces:
the processor does not depend on the core module, and Gson silently ignores JSON members it does
not recognise — so without those two tests, renaming a field in `DiscoveredExtension` would break
every extension at runtime while leaving the whole build green. If you change the descriptor format,
regenerate that file and expect both suites to move together.

The Maven plugin's descriptor needs the same kind of care. A Maven build would generate
`META-INF/maven/plugin.xml` from the `@Mojo` annotations, but the Gradle equivalent for that calls an
API Gradle 9 removed, so the file is maintained by hand under `src/main/resources`. `PluginDescriptorTest`
compares it against the mojo's fields, because a drifting descriptor fails in the *user's* build —
Maven either reports a parameter as unknown or silently never injects it.

Both build plugins deliberately write their result to a file separate from the one the annotation
processor produced, instead of editing it in place. Reading back their own output would mean that a
dependency removed from the build lingers in the descriptor forever, since the compile task stays up
to date and never regenerates it. Both have a regression test for exactly that.

Please make sure `./gradlew build` passes before opening a pull request.

## Branching and pull requests
Expand Down Expand Up @@ -76,8 +129,13 @@ refactor!: remove deprecated DemoServer entrypoint
## Releases

Releases are handled automatically: release-please opens a release PR that bumps the version in
`gradle.properties` and updates `CHANGELOG.md`. Merging that PR tags the release and publishes the
artifact to the OneLiteFeather Maven repository. Contributors do not need to bump versions manually.
`gradle.properties` and updates `CHANGELOG.md`. Merging that PR tags the release and publishes all
five modules to the OneLiteFeather Maven repository. Contributors do not need to bump versions
manually.

All modules share one version, inherited from the single `version` entry in `gradle.properties` —
they are always released together and never versioned independently. The `# x-release-please-version`
marker comment on that line is what release-please rewrites, so leave it in place.

## Reporting bugs and requesting features

Expand Down
Loading
Loading