feat(leaf): migrate distribution to Java 25 and Purpur
BREAKING CHANGE: New releases require Java 25/Purpur 26.2 and use purpur-leaf-<version>.jar. Runtime Leaf identity, data folder, protection state and scoreboard team remain unchanged. Replace the old JAR rather than installing both distributions.
This commit is contained in:
@@ -17,7 +17,7 @@ jobs:
|
|||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 17
|
java-version: 25
|
||||||
cache: gradle
|
cache: gradle
|
||||||
|
|
||||||
- name: Validate conventional commits
|
- name: Validate conventional commits
|
||||||
@@ -35,6 +35,6 @@ jobs:
|
|||||||
- name: Upload development artifact
|
- name: Upload development artifact
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: leaf-${{ github.sha }}
|
name: purpur-leaf-${{ github.sha }}
|
||||||
path: build/libs/*.jar
|
path: build/libs/*.jar
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 17
|
java-version: 25
|
||||||
cache: gradle
|
cache: gradle
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
@@ -86,8 +86,8 @@ jobs:
|
|||||||
if: steps.release.outputs.created == 'true'
|
if: steps.release.outputs.created == 'true'
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: leaf-${{ steps.release.outputs.version }}
|
name: purpur-leaf-${{ steps.release.outputs.version }}
|
||||||
path: build/libs/leaf-${{ steps.release.outputs.version }}.jar
|
path: build/libs/purpur-leaf-${{ steps.release.outputs.version }}.jar
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Create Gitea release and upload plugin
|
- name: Create Gitea release and upload plugin
|
||||||
@@ -101,7 +101,7 @@ jobs:
|
|||||||
PREVIOUS_TAG: ${{ steps.previous_tag.outputs.value }}
|
PREVIOUS_TAG: ${{ steps.previous_tag.outputs.value }}
|
||||||
run: |
|
run: |
|
||||||
api_url="${GITEA_SERVER_URL}/api/v1"
|
api_url="${GITEA_SERVER_URL}/api/v1"
|
||||||
jar="build/libs/leaf-${VERSION}.jar"
|
jar="build/libs/purpur-leaf-${VERSION}.jar"
|
||||||
export RELEASE_BODY
|
export RELEASE_BODY
|
||||||
if [ -n "$PREVIOUS_TAG" ]; then
|
if [ -n "$PREVIOUS_TAG" ]; then
|
||||||
RELEASE_BODY=$(git log --pretty='format:- %s (%h)' "${PREVIOUS_TAG}..HEAD")
|
RELEASE_BODY=$(git log --pretty='format:- %s (%h)' "${PREVIOUS_TAG}..HEAD")
|
||||||
@@ -138,4 +138,4 @@ jobs:
|
|||||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
-H "Content-Type: application/octet-stream" \
|
-H "Content-Type: application/octet-stream" \
|
||||||
--data-binary "@${jar}" \
|
--data-binary "@${jar}" \
|
||||||
"${api_url}/repos/${REPOSITORY}/releases/${release_id}/assets?name=leaf-${VERSION}.jar"
|
"${api_url}/repos/${REPOSITORY}/releases/${release_id}/assets?name=purpur-leaf-${VERSION}.jar"
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# spigot-leaf agent entrypoint
|
# purpur-leaf agent entrypoint
|
||||||
|
|
||||||
The canonical stories, engineering guidance, and **all process documents** are in the private [SoMC OKF wiki](https://git.garvis.dev/dmg/somc-okf/src/branch/main/index.md).
|
The canonical stories, engineering guidance, and **all process documents** are in the private [SoMC OKF wiki](https://git.garvis.dev/dmg/somc-okf/src/branch/main/index.md).
|
||||||
|
|
||||||
Before work, read the sibling `../somc-okf/index.md`, `../somc-okf/processes/index.md`, `../somc-okf/projects/spigot-leaf/index.md`, `engineering.md` in that project section, and relevant `../somc-okf/user-stories/spigot-leaf/` stories. Also follow the parent workspace `AGENTS.md` when present.
|
Before work, read the sibling `../somc-okf/index.md`, `../somc-okf/processes/index.md`, `../somc-okf/projects/purpur-leaf/index.md`, `engineering.md` in that project section, and relevant `../somc-okf/user-stories/purpur-leaf/` stories. Also follow the parent workspace `AGENTS.md` when present.
|
||||||
|
|
||||||
For standalone checkouts, start at the [project page](https://git.garvis.dev/dmg/somc-okf/src/branch/main/projects/spigot-leaf/index.md) and [shared process](https://git.garvis.dev/dmg/somc-okf/src/branch/main/processes/development.md). Obtain wiki access before feature work; do not recreate a local knowledge bundle. Source builds do not require private wiki access.
|
For standalone checkouts, start at the [project page](https://git.garvis.dev/dmg/somc-okf/src/branch/main/projects/purpur-leaf/index.md) and [shared process](https://git.garvis.dev/dmg/somc-okf/src/branch/main/processes/development.md). Obtain wiki access before feature work; do not recreate a local knowledge bundle. Source builds do not require private wiki access.
|
||||||
|
|
||||||
Development follows [Development cycle](https://git.garvis.dev/dmg/somc-okf/src/branch/main/runbooks/development-cycle.md): approved stories, failing tests, passing implementation, verification, then source/wiki commit and push. GitOps updates are committed locally **without pushing**; only [Do release](https://git.garvis.dev/dmg/somc-okf/src/branch/main/runbooks/do-release.md) authorizes a reviewed GitOps push.
|
Development follows [Development cycle](https://git.garvis.dev/dmg/somc-okf/src/branch/main/runbooks/development-cycle.md): approved stories, failing tests, passing implementation, verification, then source/wiki commit and push. GitOps updates are committed locally **without pushing**; only [Do release](https://git.garvis.dev/dmg/somc-okf/src/branch/main/runbooks/do-release.md) authorizes a reviewed GitOps push.
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
# Leaf
|
# Purpur Leaf
|
||||||
|
|
||||||
Leaf is a Spigot 26.2 plugin that lets non-aggressive players opt into visible Resistance and Strength protection.
|
Leaf is a Purpur 26.2 plugin that lets non-aggressive players opt into visible Resistance and Strength protection.
|
||||||
|
|
||||||
The approved behavior is specified in the [SoMC OKF wiki](https://git.garvis.dev/dmg/somc-okf/src/branch/main/projects/spigot-leaf/index.md).
|
The approved behavior is specified in the [SoMC OKF wiki](https://git.garvis.dev/dmg/somc-okf/src/branch/main/projects/purpur-leaf/index.md).
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Spigot 26.2
|
- Purpur 26.2 build 2618
|
||||||
- Java 17 or newer
|
- Java 25
|
||||||
|
|
||||||
|
Distribution repository, checkout and artifacts use `purpur-leaf`. Runtime `Leaf`, Java packages/entrypoint, `plugins/Leaf/`, commands/permissions and `leaf_protected` scoreboard team are unchanged. Replace the old JAR instead of installing both distributions. Old release tags/assets remain available. New releases no longer support Java 17.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
@@ -15,7 +17,7 @@ The approved behavior is specified in the [SoMC OKF wiki](https://git.garvis.dev
|
|||||||
./gradlew clean check jar
|
./gradlew clean check jar
|
||||||
```
|
```
|
||||||
|
|
||||||
The plugin JAR is written to `build/libs/`.
|
The plugin JAR is written to `build/libs/purpur-leaf-<version>.jar`.
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
@@ -35,9 +37,9 @@ Administrative commands require `leaf.admin`, granted to server operators by def
|
|||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
Leaf prefixes the standard Spigot display name (used by standard chat), player-list name, and a dedicated `leaf_protected` main-scoreboard team for overhead names. Legacy `&` color codes are supported. Leaf does not move players out of an existing scoreboard team, and per-viewer/custom scoreboards may not show its overhead prefix. Chat or tab-management plugins may replace Leaf's decorated names. During cleanup, Leaf removes exact copies of its configured prefix while preserving surrounding name formatting; another plugin should therefore not intentionally use identical prefix text.
|
Leaf prefixes the standard Bukkit-compatible display name (used by standard chat), player-list name, and a dedicated `leaf_protected` main-scoreboard team for overhead names. Legacy `&` color codes are supported. Leaf does not move players out of an existing scoreboard team, and per-viewer/custom scoreboards may not show its overhead prefix. Chat or tab-management plugins may replace Leaf's decorated names. During cleanup, Leaf removes exact copies of its configured prefix while preserving surrounding name formatting; another plugin should therefore not intentionally use identical prefix text.
|
||||||
|
|
||||||
Spigot identifies potion effects by type but does not expose their owning plugin. Leaf tracks the exact infinite, quiet Resistance and Strength effects it successfully installed and removes them only while each visible effect still matches. A distinct level, duration, or presentation is preserved. Spigot cannot distinguish an externally supplied effect with an identical fingerprint; Leaf therefore does not claim or later remove an identical effect that was already active when reconciliation ran.
|
The server API identifies potion effects by type but does not expose their owning plugin. Leaf tracks the exact infinite, quiet Resistance and Strength effects it successfully installed and removes them only while each visible effect still matches. A distinct level, duration, or presentation is preserved. The server API cannot distinguish an externally supplied effect with an identical fingerprint; Leaf therefore does not claim or later remove an identical effect that was already active when reconciliation ran.
|
||||||
|
|
||||||
Leaf reconciles opted-in online players approximately once per second. Effects cleared by death, milk, commands, or another plugin are restored automatically. A temporary external Resistance or Strength effect is left untouched, the leaf prefix remains visible, and Leaf restores its configured effect after the external effect ends.
|
Leaf reconciles opted-in online players approximately once per second. Effects cleared by death, milk, commands, or another plugin are restored automatically. A temporary external Resistance or Strength effect is left untouched, the leaf prefix remains visible, and Leaf restores its configured effect after the external effect ends.
|
||||||
|
|
||||||
|
|||||||
+8
-5
@@ -9,23 +9,23 @@ version = providers.gradleProperty("releaseVersion")
|
|||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
|
maven("https://repo.purpurmc.org/snapshots/")
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
toolchain {
|
toolchain {
|
||||||
languageVersion = JavaLanguageVersion.of(17)
|
languageVersion = JavaLanguageVersion.of(25)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<JavaCompile>().configureEach {
|
tasks.withType<JavaCompile>().configureEach {
|
||||||
options.compilerArgs.addAll(listOf("-Xlint:all", "-Werror"))
|
options.compilerArgs.addAll(listOf("-Xlint:all,-deprecation,-removal", "-Werror"))
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly("org.spigotmc:spigot-api:26.2-R0.1-SNAPSHOT")
|
compileOnly("org.purpurmc.purpur:purpur-api:26.2.build.2618-stable")
|
||||||
|
|
||||||
testImplementation("org.spigotmc:spigot-api:26.2-R0.1-SNAPSHOT")
|
testImplementation("org.purpurmc.purpur:purpur-api:26.2.build.2618-stable")
|
||||||
testImplementation(platform("org.junit:junit-bom:5.13.4"))
|
testImplementation(platform("org.junit:junit-bom:5.13.4"))
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||||
testImplementation("org.mockito:mockito-core:5.18.0")
|
testImplementation("org.mockito:mockito-core:5.18.0")
|
||||||
@@ -35,6 +35,9 @@ dependencies {
|
|||||||
|
|
||||||
tasks.test {
|
tasks.test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
|
dependsOn(tasks.jar)
|
||||||
|
systemProperty("distribution.jar", tasks.jar.get().archiveFile.get().asFile.absolutePath)
|
||||||
|
systemProperty("distribution.version", project.version.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
val pluginVersion = version
|
val pluginVersion = version
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
rootProject.name = "leaf"
|
rootProject.name = "purpur-leaf"
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package games.dmg.leaf;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.jar.JarFile;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
|
class PurpurDistributionTest {
|
||||||
|
@Test
|
||||||
|
void packagesNewNameWithoutChangingRuntimeIdentityOrBundlingTheApi() throws Exception {
|
||||||
|
Path artifact = Path.of(System.getProperty("distribution.jar"));
|
||||||
|
String version = System.getProperty("distribution.version");
|
||||||
|
assertEquals("purpur-leaf-" + version + ".jar", artifact.getFileName().toString());
|
||||||
|
try (var jar = new JarFile(artifact.toFile())) {
|
||||||
|
Map<?, ?> metadata = new Yaml().load(jar.getInputStream(jar.getJarEntry("plugin.yml")));
|
||||||
|
assertEquals(version, metadata.get("version"));
|
||||||
|
assertEquals("Leaf", metadata.get("name"), "Retain the data directory and persistent namespace");
|
||||||
|
assertEquals("games.dmg.leaf.LeafPlugin", metadata.get("main"));
|
||||||
|
assertTrue(((Map<?, ?>) metadata.get("permissions")).containsKey("leaf.admin"));
|
||||||
|
assertFalse(jar.stream().anyMatch(entry -> entry.getName().startsWith("org/bukkit/")
|
||||||
|
|| entry.getName().startsWith("org/purpurmc/") || entry.getName().endsWith("Test.class")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void pluginBytecodeTargetsJava25() throws Exception {
|
||||||
|
try (var jar = new JarFile(System.getProperty("distribution.jar"));
|
||||||
|
var bytecode = new DataInputStream(jar.getInputStream(
|
||||||
|
jar.getJarEntry("games/dmg/leaf/LeafPlugin.class")))) {
|
||||||
|
assertEquals(0xCAFEBABE, bytecode.readInt());
|
||||||
|
bytecode.readUnsignedShort();
|
||||||
|
assertEquals(69, bytecode.readUnsignedShort());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void releaseAndCiAgreeWithThePurpurDistributionNameAndToolchain() throws Exception {
|
||||||
|
String build = Files.readString(Path.of("build.gradle.kts"));
|
||||||
|
assertTrue(build.contains("purpur-api:26.2.build.2618-stable"));
|
||||||
|
assertFalse(build.contains("org.spigotmc:spigot-api"));
|
||||||
|
for (String workflow : new String[] {"ci.yml", "release.yml"}) {
|
||||||
|
String content = Files.readString(Path.of(".gitea/workflows", workflow));
|
||||||
|
assertTrue(content.contains("java-version: 25"));
|
||||||
|
assertTrue(content.contains("purpur-leaf-"), workflow);
|
||||||
|
assertFalse(content.contains("build/libs/leaf-") || content.contains("name: leaf-"), workflow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user