Skip to content

Commit 7c471e9

Browse files
archleviem1
andauthored
chore: simplify build configuration and update documentation (#393)
* chore(gradle): reference group variable (reduce needed editing) * chore(gradle): remove configurations.creating deprecation * chore(gradle): simplify java version * chore(gradle): fix earlier mockitoAgent revision * chore(gradle): simplify some comments * chore(gradle): simplify group access * chore(gradle): simplify shadowJar access * chore(gradle): simplify build task access * chore(CI): fix inaccurate check * chore(docs): simplify CONTRIBUTING.md reference * docs: revise fill-template-plugin skill and outdated info * docs: remove outdated info from run-plugin skill * docs: revise AGENTS.md * docs: revise docs * Update docs/customization.md Co-authored-by: Levi Muniz <levi.muniz17@gmail.com> * chore(skills): revert run-plugin example class --------- Co-authored-by: Levi Muniz <levi.muniz17@gmail.com>
1 parent 1687674 commit 7c471e9

9 files changed

Lines changed: 125 additions & 146 deletions

File tree

.agents/skills/fill-template-plugin/SKILL.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@ name: fill-template-plugin
33
description: Customize this PaperMC/Spigot plugin template for a real plugin. Use when asked to fill in, adapt, rename, fork, or customize the template project.
44
---
55

6-
If information required in the checklist below is missing, end your turn and ask for it before editing.
6+
Read `docs/customization.md` before editing. Ask for any missing plugin details before making
7+
changes.
78

8-
# Template Customization Checklist
9+
# Checklist
910

10-
When adapting this template for a real plugin, update:
11-
1. `settings.gradle.kts``rootProject.name`
12-
2. `build.gradle.kts``group` (Java package)
13-
3. `src/main/resources/plugin.yml``author`, `description`, `permissions`
14-
4. Rename the Java package and source directory from `com.crimsonwarpedcraft.exampleplugin`
15-
5. `.github/CODEOWNERS`, `.github/FUNDING.yml` — replace `leviem1`
16-
6. `CODE_OF_CONDUCT.md` line 63 — contact method
17-
7. README badges and Discord invite link
11+
1. Set `rootProject.name`; rename `ExamplePlugin.java` and all references to the example plugin.
12+
2. Set the Gradle `group`; rename main and test package paths, declarations, and imports.
13+
3. Replace or remove the example code.
14+
4. Update `plugin.yml` metadata and permissions. Do not declare commands there.
15+
5. Align the Paper API, `api-version`, Java toolchain, CI Java versions, dependencies, shading,
16+
and supported-version docs.
17+
6. Update README content and links, project docs, `AGENTS.md`, and `.agents/skills/`.
18+
7. Update GitHub ownership, funding, conduct contact, templates, policies, workflows, variables,
19+
and secrets as applicable.
20+
8. Search for template leftovers, then run `./gradlew clean build`.

.agents/skills/run-plugin/SKILL.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ folder.
2626
./gradlew test --tests "com.example.plugin.command.PingTest"
2727

2828
# Run a single test method
29-
./gradlew test --tests "com.example.plugin.command.GreetTest.greetsTarget"
29+
./gradlew test --tests "com.example.plugin.GreetTest.greetsTarget"
3030
```
3131

3232
Checkstyle enforces Google Java style with `maxWarnings = 0` — the build fails on any warning. SpotBugs runs FindSecBugs. Both run as part of `build`; fix all findings before committing.
@@ -42,8 +42,8 @@ Command executor unit tests (`Ping`, `Greet`, etc.) use Mockito directly — moc
4242

4343
Versioning logic (`build.gradle.kts`):
4444
- No `-Pver``yyMMdd-HHmm-SNAPSHOT`
45-
- `-Pver=vX.Y.Z-RC-N` `X.Y.Z-SNAPSHOT`
46-
- `-Pver=vX.Y.Z` `X.Y.Z` (stable; the `release` task then renames the shadow jar to
45+
- `-Pver=vX.Y.Z-RC-N` -> `X.Y.Z-RC-N-SNAPSHOT`
46+
- `-Pver=vX.Y.Z` -> `X.Y.Z` (stable; the `release` task then renames the shadow jar to
4747
`${rootProject.name}.jar`)
4848

4949
Quote the `-Pver` value to stop the shell/PowerShell from mangling the `=`.

.github/workflows/tag.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
files: ${{ github.workspace }}/build/libs/*
4141
generate_release_notes: true
4242
name: ${{ format('Release {0}', github.ref_name) }}
43-
prerelease: ${{ contains(github.ref_name, '-rc-') }}
43+
prerelease: ${{ contains(github.ref_name, '-RC-') }}
4444
fail_on_unmatched_files: true
4545
draft: true
4646
outputs:

AGENTS.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,31 @@
1-
This is a **PaperMC/Spigot Minecraft plugin template**. The intent is that users fork/copy it and replace the example scaffolding with their own plugin.
1+
This repository is a **PaperMC/Spigot Minecraft plugin template**. Users fork or copy it, then replace the example scaffolding with their own plugin.
22

33
## Architecture
44

5-
**Entry point**: `ExamplePlugin extends JavaPlugin`Bukkit/Paper calls `onEnable()` and `onDisable()` on the plugin lifecycle. The main class is referenced in `plugin.yml` via the `${PACKAGE}.${NAME}` substitution, which is filled at build time by `processResources` from `group` (build.gradle.kts) and `rootProject.name` (settings.gradle.kts).
5+
**Entry point**: `ExamplePlugin extends JavaPlugin`. Bukkit/Paper calls `onEnable()` and `onDisable()` during the plugin lifecycle. In `plugin.yml`, `${PACKAGE}.${NAME}` identifies the main class. The `processResources` task fills these placeholders at build time from `group` in `build.gradle.kts` and `rootProject.name` in `settings.gradle.kts`.
66

7-
**JAR packaging**: The standard `jar` task is disabled. `shadowJar` is the sole output — it shades CommandAPI (relocated to `<group>.commandapi`). CommandAPI is excluded from `minimize()` because it loads classes via reflection. `assemble` depends on `shadowJar`.
7+
**JAR packaging**: The standard `jar` task is disabled, and `shadowJar` is the sole output. It shades CommandAPI and relocates it to `<group>.commandapi`. CommandAPI is excluded from `minimize()` because it loads classes through reflection. `assemble` depends on `shadowJar`.
88

9-
**cw-commons dependency**: `Command`/`BaseCommand` (command registration) and `Config`/`ConfigManager` (YAML config loading + Jakarta validation) come from [cw-commons](https://github.com/CrimsonWarpedcraft/cw-commons), not local code — consumed via JitPack (`com.github.CrimsonWarpedcraft:cw-commons`). `build.gradle.kts` pins a tagged release (e.g. `v0.1.0`) rather than the unstable `main-SNAPSHOT`; bump that tag deliberately. Jackson and Hibernate Validator remain direct dependencies here because `PluginConfig` uses their annotations (`@JsonProperty`, `@NotBlank`) directly — cw-commons exposes them as `api` (transitive, unbundled) dependencies specifically so each consumer shades/relocates its own copy without classloader conflicts. Note that cw-commons' `api` deps already put Jackson/Hibernate Validator on this project's classpath transitively, so the explicit declarations here are redundant for resolution — kept anyway since `PluginConfig` references them directly and shouldn't rely on another project's transitive exposure choices.
9+
**cw-commons dependency**: [cw-commons](https://github.com/CrimsonWarpedcraft/cw-commons) provides `Command`/`BaseCommand` for command registration and `Config`/`ConfigManager` for YAML loading and Jakarta validation. These classes are not defined locally. The project consumes cw-commons from JitPack as `com.github.CrimsonWarpedcraft:cw-commons`. `build.gradle.kts` pins a tagged release, such as `v0.1.0`, instead of the unstable `main-SNAPSHOT`. Update that tag deliberately.
10+
11+
Jackson and Hibernate Validator remain direct dependencies because `PluginConfig` uses their `@JsonProperty` and `@NotBlank` annotations. cw-commons exposes these libraries as transitive, unbundled `api` dependencies so each consumer can shade and relocate its own copy without classloader conflicts. The direct declarations are redundant for dependency resolution, but they prevent this project from relying on another project's transitive exposure choices.
1012

1113
**Command declaration**: CommandAPI registers commands programmatically in `onEnable()` (see `ExampleCommand`/`BaseCommand`). Adding a matching entry under `commands:` in `plugin.yml` makes Bukkit register the same command a second time, which CommandAPI flags at startup with a "Plugin command ... is registered by Bukkit" warning. `permissions:` entries are unaffected and still required.
1214

1315
**Versioning logic** (in `build.gradle.kts`):
14-
- No `-Pver` supplied → `yyMMdd-HHmm-SNAPSHOT`
15-
- `-Pver=vX.Y.Z-RC-N``X.Y.Z-SNAPSHOT`
16-
- `-Pver=vX.Y.Z``X.Y.Z` (stable release)
16+
17+
- No `-Pver` supplied -> `yyMMdd-HHmm-SNAPSHOT`
18+
- `-Pver=vX.Y.Z-RC-N` -> `X.Y.Z-RC-N-SNAPSHOT`
19+
- `-Pver=vX.Y.Z` -> `X.Y.Z` (stable release)
1720

1821
**CI workflows** (`.github/workflows/`):
19-
- `pr.yml` — builds and tests on Ubuntu + Windows for PRs and merge queue
20-
- `main.yml` — builds, tests, and cuts a snapshot release on push to `main`
21-
- `tag.yml` / `release.yml` — handle tagged releases and Discord notifications
2222

23-
**Agent instructions**: (1) Canonical skills live in `.agents/skills/`; `.claude/skills/` is a generated mirror. (2) `CLAUDE.md` is a generated copy of this `AGENTS.md`. (3) No agents are permitted to edit or create `CLAUDE.md` or `.claude/skills/`; Claude hooks established in `.claude/settings.json` automatically sync these mirrors on `SessionStart` and `PostToolUse`.
23+
- `pr.yml`: builds and tests on Ubuntu + Windows for PRs and merge queue
24+
- `main.yml`: builds, tests, and uploads a snapshot artifact on push to `main`
25+
- `tag.yml` / `release.yml`: handle tagged releases and Discord notifications
26+
27+
## Agent instructions
28+
29+
1. Canonical skills live in `.agents/skills/`. The `.claude/skills/` directory is a generated mirror.
30+
2. `CLAUDE.md` is a generated copy of this `AGENTS.md`.
31+
3. Do not edit or create `CLAUDE.md` or files under `.claude/skills/`. Claude hooks configured in `.claude/settings.json` synchronize these mirrors on `SessionStart` and `PostToolUse`.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,4 @@ This build step will also run all checks and tests, making sure your code is cle
6363
JARs can be found in `build/libs/`.
6464

6565
## Contributing
66-
See [CONTRIBUTING.md](https://github.com/CrimsonWarpedcraft/plugin-template/blob/main/CONTRIBUTING.md).
66+
See [CONTRIBUTING.md](CONTRIBUTING.md).

build.gradle.kts

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,7 @@ version = (if (!hasProperty("ver")) {
2727
if (ver.startsWith("v") && !ver.lowercase().contains("-rc-")) base else "$base-SNAPSHOT"
2828
}).uppercase()
2929

30-
java {
31-
sourceCompatibility = JavaVersion.VERSION_25
32-
targetCompatibility = JavaVersion.VERSION_25
33-
}
30+
java.toolchain.languageVersion = JavaLanguageVersion.of(25)
3431

3532
repositories {
3633
maven {
@@ -62,7 +59,7 @@ repositories {
6259
}
6360
}
6461

65-
val mockitoAgent by configurations.creating
62+
val mockitoAgent = configurations.create("mockitoAgent")
6663

6764
dependencies {
6865
compileOnly("io.papermc.paper:paper-api:26.1.2.build.72-stable")
@@ -75,17 +72,13 @@ dependencies {
7572
testImplementation("org.junit.jupiter:junit-jupiter:6.1.0")
7673
testRuntimeOnly("org.junit.platform:junit-platform-launcher:6.1.0")
7774

78-
79-
// Dependencies used by the example code. Not required for Paper plugins.
75+
// Example dependencies. Paper plugins do not require these libraries.
8076
implementation("com.github.CrimsonWarpedcraft:cw-commons:v0.1.1")
81-
// Jackson + Hibernate Validator: also exposed transitively via cw-commons' `api` deps,
82-
// but declared directly anyway since PluginConfig imports their annotations — don't
83-
// rely on a transitive exposure decision made by another project for code we compile against.
77+
// PluginConfig imports annotations from Jackson and Hibernate Validator directly.
8478
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.22.0")
85-
// Example command implementation via CommandAPI
86-
// https://commandapi.jorel.dev
8779
implementation("dev.jorel:commandapi-paper-shade:11.2.0")
8880
implementation("org.hibernate.validator:hibernate-validator:9.1.1.Final")
81+
8982
testImplementation("org.mockito:mockito-core:5.23.0")
9083
mockitoAgent("org.mockito:mockito-core:5.23.0") { isTransitive = false }
9184
}
@@ -97,7 +90,7 @@ tasks.test {
9790

9891
tasks.processResources {
9992
filesMatching("**/plugin.yml") {
100-
expand(mapOf("NAME" to rootProject.name, "VERSION" to version, "PACKAGE" to rootProject.group.toString()))
93+
expand(mapOf("NAME" to rootProject.name, "VERSION" to version, "PACKAGE" to project.group))
10194
}
10295
}
10396

@@ -129,16 +122,15 @@ tasks.withType<SpotBugsTask>().configureEach {
129122
}
130123
}
131124

132-
tasks.named<ShadowJar>("shadowJar") {
125+
val shadowJar = tasks.named<ShadowJar>("shadowJar") {
133126
archiveClassifier.set("")
134127
mergeServiceFiles()
135-
// Update the destination package to match your group when renaming the plugin
136-
relocate("dev.jorel.commandapi", "com.crimsonwarpedcraft.exampleplugin.commandapi")
137-
relocate("com.fasterxml", "com.crimsonwarpedcraft.exampleplugin.fasterxml")
138-
relocate("org.yaml.snakeyaml", "com.crimsonwarpedcraft.exampleplugin.snakeyaml")
139-
relocate("org.hibernate.validator", "com.crimsonwarpedcraft.exampleplugin.hibernatevalidator")
140-
relocate("jakarta.validation", "com.crimsonwarpedcraft.exampleplugin.jakartavalidation")
141-
relocate("org.jboss.logging", "com.crimsonwarpedcraft.exampleplugin.jbosslogging")
128+
relocate("dev.jorel.commandapi", "${project.group}.commandapi")
129+
relocate("com.fasterxml", "${project.group}.fasterxml")
130+
relocate("org.yaml.snakeyaml", "${project.group}.snakeyaml")
131+
relocate("org.hibernate.validator", "${project.group}.hibernatevalidator")
132+
relocate("jakarta.validation", "${project.group}.jakartavalidation")
133+
relocate("org.jboss.logging", "${project.group}.jbosslogging")
142134
// These libs load classes via reflection or SPI and must not be minimized
143135
minimize {
144136
exclude(dependency("dev.jorel:commandapi-paper-shade:.*"))
@@ -160,7 +152,7 @@ tasks.jar {
160152
}
161153

162154
tasks.assemble {
163-
dependsOn(tasks.named("shadowJar"))
155+
dependsOn(shadowJar)
164156
}
165157

166158
tasks.register("printProjectName") {
@@ -170,13 +162,12 @@ tasks.register("printProjectName") {
170162
}
171163

172164
tasks.register("release") {
173-
dependsOn(tasks.named("build"))
165+
dependsOn("build")
174166

175167
doLast {
176168
if (!version.toString().endsWith("-SNAPSHOT")) {
177-
// Rename final JAR to trim off version information
178-
tasks.named<ShadowJar>("shadowJar").get().archiveFile.get().asFile
179-
.renameTo(layout.buildDirectory.get().asFile.resolve("libs/${rootProject.name}.jar"))
169+
val releaseJar = layout.buildDirectory.file("libs/${rootProject.name}.jar").get().asFile
170+
shadowJar.get().archiveFile.get().asFile.renameTo(releaseJar)
180171
}
181172
}
182173
}

docs/customization.md

Lines changed: 46 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,65 @@
11
# Customizing This Template
22

3-
When adapting this template for your own plugin, you'll need to update the following files.
3+
Use this checklist when turning the template into a plugin.
44

5-
### Discord Notifications
6-
This repo allows automatically pushing releases to a Discord webhook.
5+
## Plugin identity
76

8-
To use this Action, you will need to set two GitHub Actions secrets.
9-
- `DISCORD_WEBHOOK_ID`
10-
- `DISCORD_WEBHOOK_TOKEN`
7+
1. Set `rootProject.name` in `settings.gradle.kts` to the Java entry point class name.
8+
2. Rename `ExamplePlugin.java`, the `ExamplePlugin` class, and all references to it. The current
9+
`plugin.yml` build substitution requires this name to match `rootProject.name`.
10+
3. Set `group` in `build.gradle.kts` to the Java package.
11+
4. Rename the main and test package directories, declarations, and imports from
12+
`com.crimsonwarpedcraft.exampleplugin`.
1113

12-
You can find these values by copying the Discord Webhook URL:
13-
`https://discord.com/api/webhooks/<DISCORD_WEBHOOK_ID>/<DISCORD_WEBHOOK_TOKEN>`
14+
## Example code
1415

15-
Optionally, you can also configure `DISCORD_RELEASE_WEBHOOK_ID` and `DISCORD_RELEASE_WEBHOOK_TOKEN`
16-
to send release announcements to a separate channel.
16+
Replace or remove the example command, permission, config, data store, listener, and tests. Keep
17+
these parts in sync:
1718

18-
For more information, see [Discord Message Notify](https://github.com/marketplace/actions/discord-message-notify).
19+
- Command names and permission checks in Java
20+
- Permission declarations in `src/main/resources/plugin.yml`
21+
- Fields in `PluginConfig` and `src/main/resources/config.yml`
22+
- Main and test code
1923

20-
### `README.md`
21-
Make this relevant to your project.
24+
CommandAPI registers commands in Java. Do not add matching entries under `commands:` in
25+
`plugin.yml`.
2226

23-
Be sure to replace the badges for build status and Discord.
27+
## Metadata and build
2428

25-
### `settings.gradle.kts`
26-
Replace `ExamplePlugin` with the name of your plugin.
29+
- Update `author`, `description`, `permissions`, and `api-version` in `plugin.yml`.
30+
- Keep the Paper API version, Java toolchain, CI Java versions, and documented server support in
31+
sync.
32+
- Review repositories, dependencies, Shadow relocations, and `minimize` exclusions. Remove
33+
example dependencies the plugin no longer uses.
2734

28-
```kotlin
29-
rootProject.name = "ExamplePlugin"
30-
```
35+
## Project files
3136

32-
### `build.gradle.kts`
33-
Make sure to update `group` to your package's name in the following section.
37+
- Rewrite `README.md` for the plugin. Replace the build badge, Discord link, commands, features,
38+
and repository links.
39+
- Update `docs/usage.md`, `docs/releases.md`, `AGENTS.md`, and canonical skills under
40+
`.agents/skills/` when their examples or architecture change.
41+
- Do not edit `CLAUDE.md` or `.claude/skills/`. They are generated mirrors.
42+
- Check source attribution and license terms before changing copyright notices.
3443

35-
```kotlin
36-
group = "com.crimsonwarpedcraft.exampleplugin"
37-
```
44+
## GitHub
3845

39-
Add any required repositories for your dependencies:
46+
- Update `.github/CODEOWNERS` and update or delete `.github/FUNDING.yml`.
47+
- Replace the enforcement contact in `CODE_OF_CONDUCT.md`.
48+
- Review issue templates, labels, the stale policy, Dependabot, branch protection, and workflows.
49+
- Update each `main` reference if the repository uses a different default branch.
4050

41-
```kotlin
42-
repositories {
43-
// ...
44-
}
45-
```
51+
Discord notifications use:
4652

47-
Also, update your dependencies as needed (of course).
53+
- Repository variable `DISCORD_WEBHOOK_ID`
54+
- Actions secret `DISCORD_WEBHOOK_TOKEN`
55+
- Optional repository variable `DISCORD_RELEASE_WEBHOOK_ID`
56+
- Optional Actions secret `DISCORD_RELEASE_WEBHOOK_TOKEN`
4857

49-
```kotlin
50-
dependencies {
51-
// ...
52-
}
53-
```
58+
Remove the notification jobs if the plugin will not use Discord webhooks.
5459

55-
### `src/main/resources/plugin.yml`
56-
First, update the following with your information.
60+
## Verify
5761

58-
```yaml
59-
author: AUTHOR
60-
description: DESCRIPTION
61-
```
62-
63-
Next, the `permissions` section below should be updated as needed.
64-
65-
```yaml
66-
permissions:
67-
example.test:
68-
description: DESCRIPTION
69-
default: true
70-
example.*:
71-
description: Grants all other permissions
72-
default: false
73-
children:
74-
example.test: true
75-
```
76-
77-
Do NOT create a `commands:` section — CommandAPI registers commands programmatically in
78-
`onEnable()` (see `ExampleCommand`), not via `plugin.yml`.
79-
80-
Declaring a command in both places
81-
causes Bukkit to register it a second time, which CommandAPI will warn about at startup.
82-
83-
### `.github/`
84-
- `CODEOWNERS` -> Replace `leviem1` with your username.
85-
- `FUNDING.yml` -> Update or delete this file, [whatever applies to you.](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository)
86-
87-
### Code of Conduct
88-
If you choose to adopt the Code of Conduct for your project,
89-
please update line 63 of `CODE_OF_CONDUCT.md` with your preferred contact method.
62+
1. Search for old names, packages, permissions, placeholders, attribution, and repository URLs.
63+
2. Run `./gradlew clean build`.
64+
3. Check the processed `plugin.yml` and shaded JAR for the correct main class.
65+
4. Start the JAR on the oldest supported Paper version.

0 commit comments

Comments
 (0)