minecraft-server/docs/ITZG-CUSTOM-JAR-PERSISTENCE.md
s8n 4116d67eaf feat(shop): stage ExcellentShop+CoinsEngine migration bundle
Replaces EZShop 1.0-SNAPSHOT (bespoke, sell-only, 27-slot) and Kiranhart
AuctionHouse 1.4.6 (ARR no-LICENSE, dupe history) with a single GPL-3
stack: ExcellentShop 5.0.1 + CoinsEngine 2.7.0 + nightcore 2.15.3.

Per SHOP-SYSTEM-DECISION.md (commit 9565f0b), Stack A wins on three
counts: GPL-3 source (vs ARR/proprietary), unified theme across shop
and AH, single-vendor support story under NightExpress.

Jars sourced from upstream Reposilite repo.nightexpressdev.com — same
artefacts a local mvn package would produce, just reproducible without
the alex9849 integration that breaks on TLS handshake. SHA256SUMS
committed for receipt; never re-fetched at swap time.

Bundle:
  build/    — three jars + SHA256SUMS (~2.1MB total)
  configs/  — post-first-run overrides (chest module off, /shop alias,
              AH 1% tax + BIN+bid + 10 listings matches Kiranhart)
  scripts/  — swap.sh, rollback.sh, lp-shop-migration.sh,
              lp-shop-rollback.sh, docker-compose.patch.yml

itzg integration: COPY_PLUGINS_SRC=/plugins-custom mount per
ITZG-CUSTOM-JAR-PERSISTENCE.md, plus REMOVE_OLD_MODS_EXCLUDE expanded
with the three new globs.

LP migration grants default-tier excellentshop.* + coinsengine.*
nodes; staff tier gets *.admin equivalents to legacy
auctionhouse.moderator. Group prefixes/suffixes untouched per
feedback_lp_prefixes_locked.md.

DOES NOT touch live /data/plugins/EZShop or /data/plugins/AuctionHouse
— staging only. Operator runs swap.sh in scheduled maintenance window.

Refs: SHOP-SYSTEM-DECISION.md, AUDIT-2026-05-07.md F-11,
ITZG-CUSTOM-JAR-PERSISTENCE.md, MIGRATION-PLAN-EXCELLENTSHOP.md.
2026-05-08 00:01:53 +01:00

115 lines
5.1 KiB
Markdown

# itzg/minecraft-server — Custom Plugin Jar Persistence
**Date:** 2026-05-07
**Context:** `ChatChat-1.0.0-SNAPSHOT-racked-1.jar` was dropped manually into `/data/plugins/` and disappeared after the next container restart.
---
## 1. Why the ChatChat jar disappeared
itzg's entrypoint runs this sequence on every start (when `REMOVE_OLD_MODS=TRUE`):
1. **Wipe** — every file in `/data/plugins/` matching `REMOVE_OLD_MODS_INCLUDE` and not matching `REMOVE_OLD_MODS_EXCLUDE` is deleted.
2. **Download** — every URL in `PLUGINS` and every Modrinth project in `MODRINTH_PROJECTS` is fetched into `/data/plugins/`.
3. **Copy** — anything listed in `COPY_PLUGINS_SRC` (or container paths inside `PLUGINS`) is rsynced in.
Our current compose has:
```yaml
REMOVE_OLD_MODS: "true"
REMOVE_OLD_MODS_INCLUDE: "*.jar"
REMOVE_OLD_MODS_EXCLUDE: "AuthLimbo*.jar"
```
So step 1 deletes **every** `*.jar` except `AuthLimbo*.jar`. The hand-placed ChatChat jar was not in any download list and not in the exclude glob, so it was nuked and never re-downloaded. AuthLimbo survives only because we explicitly excluded it.
This is documented behaviour, not a bug — itzg's design assumes plugins are declarative, sourced from URLs/Modrinth/`COPY_PLUGINS_SRC`, never hand-dropped.
## 2. Three mechanisms to make custom jars persist
| # | Mechanism | How |
|---|-----------|-----|
| A | **`REMOVE_OLD_MODS_EXCLUDE` glob** | Add `ChatChat*.jar` to the exclude list. Quick but fragile — depends on filename and only protects already-present files; doesn't handle re-deploy on a fresh volume. |
| B | **`COPY_PLUGINS_SRC` bind-mount** | Mount a host dir of custom jars read-only at e.g. `/plugins-custom`, set `COPY_PLUGINS_SRC=/plugins-custom`. Entrypoint copies them in after the wipe. Survives wipes, version-controllable, declarative. |
| C | **`PLUGINS` URL → Forgejo Release** | Upload the jar as a Forgejo Release asset, add the download URL to the existing `PLUGINS` list. Same flow as EssentialsX/spark already use. |
Note: `PLUGINS` also accepts container paths directly (e.g. `PLUGINS=/plugins-custom/ChatChat.jar`), so mechanism B can collapse into the existing `PLUGINS` env if preferred.
## 3. Recommended path for racked.ru — Mechanism B
`COPY_PLUGINS_SRC` is the cleanest fit:
- Custom jars live in the repo (or `/opt/docker/minecraft-custom-plugins/`), so they're under version control / backup.
- No external host dependency (Forgejo could be down — bind mount can't be).
- Build artefacts from `staging/chatchat/` drop straight into the mounted dir.
### docker-compose.yml diff
```diff
MODRINTH_LOADER: paper
SPIGET_RESOURCES: ""
REMOVE_OLD_MODS: "true"
REMOVE_OLD_MODS_INCLUDE: "*.jar"
- REMOVE_OLD_MODS_EXCLUDE: "AuthLimbo*.jar"
+ REMOVE_OLD_MODS_EXCLUDE: "AuthLimbo*.jar,ChatChat*.jar"
+ COPY_PLUGINS_SRC: "/plugins-custom"
volumes:
- /opt/docker/minecraft:/data
+ - /opt/docker/minecraft-custom-plugins:/plugins-custom:ro
```
Then on the host:
```bash
sudo mkdir -p /opt/docker/minecraft-custom-plugins
sudo cp staging/chatchat/ChatChat-1.0.0-SNAPSHOT-racked-1.jar \
/opt/docker/minecraft-custom-plugins/
sudo cp /opt/docker/minecraft/plugins/AuthLimbo-*.jar \
/opt/docker/minecraft-custom-plugins/ # optional: source-of-truth
sudo chown -R 1000:1000 /opt/docker/minecraft-custom-plugins
docker compose up -d --force-recreate mc
```
The `EXCLUDE` line still lists `ChatChat*.jar` so that if the bind mount ever vanishes, an existing copy in `/data/plugins/` isn't wiped — belt and braces.
## 4. Bonus — Forgejo Release upload procedure (mechanism C)
If you'd rather host the jar at `git.s8n.ru` (e.g. for cobblestone or a friend's box without the bind mount):
```bash
# 1. Tag and push
cd staging/chatchat
git tag -a chatchat-racked-1 -m "ChatChat racked build 1"
git push origin chatchat-racked-1
# 2. Create release + upload asset (uses Forgejo PAT from ~/.config/veilor-forgejo)
TOKEN=$(cat ~/.config/veilor-forgejo/pat)
curl -X POST \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
https://git.s8n.ru/api/v1/repos/s8n/minecraft-server/releases \
-d '{"tag_name":"chatchat-racked-1","name":"ChatChat racked-1","draft":false}'
RELEASE_ID=$(curl -s -H "Authorization: token $TOKEN" \
https://git.s8n.ru/api/v1/repos/s8n/minecraft-server/releases/tags/chatchat-racked-1 \
| jq -r .id)
curl -X POST \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/java-archive" \
--data-binary @ChatChat-1.0.0-SNAPSHOT-racked-1.jar \
"https://git.s8n.ru/api/v1/repos/s8n/minecraft-server/releases/$RELEASE_ID/assets?name=ChatChat-1.0.0-SNAPSHOT-racked-1.jar"
```
Then add to compose:
```yaml
PLUGINS: |
...existing...
https://git.s8n.ru/s8n/minecraft-server/releases/download/chatchat-racked-1/ChatChat-1.0.0-SNAPSHOT-racked-1.jar
```
## References
- itzg docs: https://docker-minecraft-server.readthedocs.io/en/latest/mods-and-plugins/
- Source: https://github.com/itzg/docker-minecraft-server/blob/master/docs/mods-and-plugins/index.md
- Issue #310 (COPY_PLUGINS_SRC behaviour): https://github.com/itzg/docker-minecraft-server/issues/310