Add a version port script and a compile check for pull requests
Check / compile (pull_request) Successful in 18m7s
Check / compile (pull_request) Successful in 18m7s
- scripts/port.sh ports a change made under versions/<from>/src to the other versions with `git apply --3way`: clean where files match, normal conflicts only where a version really differs. Supports uncommitted changes or --commit, --to, --dry-run, and detects changes already applied. - .gitea/workflows/check.yml compiles every version on pull requests and pushes to master (--continue reports all failing versions). - VERSION_GUIDE.md documents the layout, porting, and adding a version. Closes #17, closes #18 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
name: Check
|
||||
|
||||
# Compiles every Minecraft version on pull requests and on pushes to master,
|
||||
# so a change that breaks one version (e.g. a missed or bad port) fails before
|
||||
# it's merged instead of in the release build.
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set up Java 21
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 21
|
||||
cache: gradle
|
||||
|
||||
- name: Compile all versions
|
||||
run: |
|
||||
chmod +x gradlew
|
||||
# --continue reports every version that fails, not just the first.
|
||||
./gradlew compileJava --no-daemon --stacktrace --continue
|
||||
+42
-1
@@ -1,3 +1,44 @@
|
||||
# Version guide
|
||||
|
||||
This guide will help you to add or edit minecraft versions.
|
||||
How Saturn Client supports several Minecraft versions, and how to change or add one.
|
||||
|
||||
## Layout
|
||||
|
||||
- `common/`: all version-independent code (mods, UI, cosmetics, the server client). It has no Minecraft dependency and reaches the game only through the interfaces in `org.saturnclient.common`.
|
||||
- `versions/<mc>/`: one module per Minecraft version, implementing those interfaces (providers, refs, mixins). Each is a full copy, and they differ only where Minecraft's API differs.
|
||||
- `gradle.properties`: the Minecraft, Yarn, Fabric Loader and Fabric API versions.
|
||||
- `src/`: the version-specific code and the three mixin configs.
|
||||
|
||||
`settings.gradle` includes every folder under `versions/` automatically, as the Gradle project `:mc-<mc>`.
|
||||
|
||||
## Porting a change to the other versions
|
||||
|
||||
Make the change in one version, usually the newest, then run:
|
||||
|
||||
```sh
|
||||
scripts/port.sh # port uncommitted changes to every other version
|
||||
scripts/port.sh --dry-run # see what would apply cleanly first
|
||||
scripts/port.sh --to 1.21.9,1.21.10 # only some versions
|
||||
scripts/port.sh --commit <rev> # port what a commit changed instead
|
||||
```
|
||||
|
||||
The script applies the diff under `versions/<from>/src` to each other version with `git apply --3way`:
|
||||
|
||||
- **applied**: the files matched, and nothing else is needed.
|
||||
- **CONFLICTS**: that version's code really differs. The file gets normal conflict markers, with the version's own code in `ours` and the ported change in `theirs`. Adapt the change to that version's API and resolve.
|
||||
- **already has this change**: nothing to do, which is safe to see when re-running.
|
||||
|
||||
Only `src/` is ported. `build.gradle` and `gradle.properties` are version-specific on purpose. Then build everything:
|
||||
|
||||
```sh
|
||||
./gradlew compileJava
|
||||
```
|
||||
|
||||
The **Check** workflow runs the same compile on every pull request, so a version that was missed or mis-ported fails before merging.
|
||||
|
||||
## Adding a Minecraft version
|
||||
|
||||
1. Copy the closest existing version: `cp -r versions/1.21.11 versions/<new>`. Delete its `build/`, `.gradle/` and `run/` folders if present.
|
||||
2. In `versions/<new>/gradle.properties`, set `minecraft_version`, `yarn_mappings`, `loader_version` and `fabric_api_version`. The right values are listed at <https://fabricmc.net/develop>.
|
||||
3. Build it with `./gradlew :mc-<new>:compileJava` and fix what Minecraft changed. Check the mixin configs in `src/main/resources/*.mixins.json` as well: a mixin whose target changed fails at startup, not at compile time, so launch it with `./gradlew :mc-<new>:runClient`.
|
||||
4. The release workflow builds every folder under `versions/`, and "Publish to Modrinth" takes the game version from each jar's name, so neither needs changing.
|
||||
|
||||
Executable
+156
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env bash
|
||||
# Port a change made in one Minecraft version's code to the other versions.
|
||||
#
|
||||
# Takes the diff under versions/<from>/src and applies it to versions/<to>/src
|
||||
# with `git apply --3way`: it applies cleanly where the files match, and leaves
|
||||
# normal merge conflicts only where a version really differs. Only src/ is
|
||||
# ported; build.gradle and gradle.properties are version-specific on purpose.
|
||||
#
|
||||
# Usage:
|
||||
# scripts/port.sh [--from VERSION] [--to V1,V2,...] [--commit REV] [--dry-run]
|
||||
#
|
||||
# --from version the change was made in. Default: the only version with
|
||||
# uncommitted changes (or changed by --commit).
|
||||
# --to comma-separated versions to port to. Default: all others.
|
||||
# --commit port the changes a commit made instead of uncommitted changes.
|
||||
# --dry-run report what would happen without changing anything.
|
||||
#
|
||||
# Exit status: 0 if every version applied cleanly (or already had the change),
|
||||
# 1 if any version has conflicts or failed.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
cd "$(git rev-parse --show-toplevel)"
|
||||
|
||||
from=""
|
||||
to=""
|
||||
commit=""
|
||||
dry_run=false
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--from) from="$2"; shift 2 ;;
|
||||
--to) to="$2"; shift 2 ;;
|
||||
--commit) commit="$2"; shift 2 ;;
|
||||
--dry-run) dry_run=true; shift ;;
|
||||
-h|--help) sed -n '2,20p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||||
*) echo "Unknown option: $1 (see --help)" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
all_versions=$(ls -d versions/*/ | xargs -n1 basename | sort -t. -k1,1n -k2,2n -k3,3n)
|
||||
|
||||
version_exists() {
|
||||
[ -d "versions/$1/src" ]
|
||||
}
|
||||
|
||||
# Versions with changes, from the working tree or from the given commit.
|
||||
changed_versions() {
|
||||
if [ -n "$commit" ]; then
|
||||
git show --name-only --format= "$commit" -- versions/
|
||||
else
|
||||
git diff HEAD --name-only -- versions/
|
||||
git ls-files --others --exclude-standard -- versions/
|
||||
fi | sed -n 's#^versions/\([^/]*\)/src/.*#\1#p' | sort -u
|
||||
}
|
||||
|
||||
if [ -z "$from" ]; then
|
||||
candidates=$(changed_versions)
|
||||
count=$(printf '%s\n' "$candidates" | grep -c . || true)
|
||||
if [ "$count" -ne 1 ]; then
|
||||
echo "Changes found in ${count} versions (${candidates//$'\n'/, }); pass --from to choose one." >&2
|
||||
exit 2
|
||||
fi
|
||||
from="$candidates"
|
||||
fi
|
||||
|
||||
version_exists "$from" || { echo "No such version: $from" >&2; exit 2; }
|
||||
|
||||
if [ -z "$to" ]; then
|
||||
to=$(printf '%s\n' $all_versions | grep -vx "$from" | paste -sd, -)
|
||||
fi
|
||||
|
||||
# New files are only included in `git diff HEAD` once git knows about them.
|
||||
if [ -z "$commit" ]; then
|
||||
untracked=$(git ls-files --others --exclude-standard -- "versions/$from/src")
|
||||
if [ -n "$untracked" ]; then
|
||||
if $dry_run; then
|
||||
echo "note: new files in versions/$from/src are untracked and won't be ported in a dry run:" >&2
|
||||
printf ' %s\n' $untracked >&2
|
||||
else
|
||||
printf '%s\n' "$untracked" | xargs git add --intent-to-add --
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# --full-index gives --3way the real blob ids to merge against.
|
||||
if [ -n "$commit" ]; then
|
||||
patch=$(git show --full-index --binary --format= "$commit" -- "versions/$from/src")
|
||||
else
|
||||
patch=$(git diff HEAD --full-index --binary -- "versions/$from/src")
|
||||
fi
|
||||
|
||||
if [ -z "$patch" ]; then
|
||||
echo "No changes under versions/$from/src to port." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
files=$(printf '%s\n' "$patch" | grep -c '^diff --git' || true)
|
||||
echo "Porting $files file(s) from $from${commit:+ (commit $commit)}"
|
||||
|
||||
from_re=$(printf '%s' "$from" | sed 's/\./\\./g')
|
||||
tmp=$(mktemp -d)
|
||||
trap 'rm -rf "$tmp"' EXIT
|
||||
|
||||
status=0
|
||||
IFS=',' read -r -a targets <<< "$to"
|
||||
|
||||
for target in "${targets[@]}"; do
|
||||
if [ "$target" = "$from" ]; then
|
||||
continue
|
||||
fi
|
||||
if ! version_exists "$target"; then
|
||||
printf ' %-8s skipped: no such version\n' "$target"
|
||||
status=1
|
||||
continue
|
||||
fi
|
||||
|
||||
# Point the patch's file headers at the target version.
|
||||
printf '%s\n' "$patch" | sed -E "/^(diff --git |--- |\+\+\+ |rename (from|to) |copy (from|to) )/ s#versions/${from_re}/#versions/${target}/#g" > "$tmp/$target.patch"
|
||||
|
||||
if git apply --check --reverse "$tmp/$target.patch" 2>/dev/null; then
|
||||
printf ' %-8s already has this change\n' "$target"
|
||||
continue
|
||||
fi
|
||||
|
||||
if $dry_run; then
|
||||
if git apply --check "$tmp/$target.patch" 2>/dev/null; then
|
||||
printf ' %-8s would apply cleanly\n' "$target"
|
||||
else
|
||||
printf ' %-8s would need a 3-way merge (possible conflicts)\n' "$target"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
if output=$(git apply --3way "$tmp/$target.patch" 2>&1); then
|
||||
printf ' %-8s applied\n' "$target"
|
||||
else
|
||||
conflicts=$(git diff --name-only --diff-filter=U -- "versions/$target")
|
||||
if [ -n "$conflicts" ]; then
|
||||
printf ' %-8s CONFLICTS in:\n' "$target"
|
||||
printf '%s\n' "$conflicts" | sed 's/^/ /'
|
||||
else
|
||||
printf ' %-8s FAILED:\n' "$target"
|
||||
printf '%s\n' "$output" | sed 's/^/ /'
|
||||
fi
|
||||
status=1
|
||||
fi
|
||||
done
|
||||
|
||||
if ! $dry_run; then
|
||||
echo
|
||||
echo "Ported changes are staged in the target versions. Resolve any conflicts, then build"
|
||||
echo "the affected versions, e.g. ./gradlew compileJava"
|
||||
fi
|
||||
|
||||
exit $status
|
||||
Reference in New Issue
Block a user