build: rebase patches onto new upstream releases

This commit is contained in:
Felitendo committed 2026-09-15 15:02:13 +02:00
1 parent 7e02e6fa9b
commit 76c250aa2f
5 files changed
+73 -17

No files matched your search

+7 -5
View File
@@ -181,10 +181,12 @@ cd ../..
scripts/export-patches.sh # rewrite patches/ from those commits
```
Patches are applied with `git am --3way`, so small upstream movements around a hunk resolve by
themselves. When one genuinely conflicts, `scripts/prepare.sh` stops and leaves the conflict staged
in `build/upstream` to be resolved with `git am --continue`, after which `scripts/export-patches.sh`
writes the fixed series back.
Patches are applied to the release they were exported against (`patches/base.txt`), where they
always fit, and then rebased onto the release in `upstream.txt` when that is newer. The rebase
merges against the files the patches were written for, so upstream changes near a hunk resolve by
themselves. When one genuinely conflicts, `scripts/prepare.sh` stops with the rebase in progress in
`build/upstream`, to be resolved with `git rebase --continue`, after which
`scripts/export-patches.sh` writes the fixed series back against the new release.
To move to a newer upstream release:
@@ -199,7 +201,7 @@ scripts/prepare.sh
reusable workflow the release job calls. It applies the patches, checks them, and builds on
Linux, Windows and macOS.
- **Upstream release** (`.github/workflows/upstream-release.yml`) runs daily. If Modrinth has
published a newer release than `upstream.txt`, it rebuilds against it and — only if every
published a newer release than `upstream.txt`, it rebases the patches onto it, rebuilds and — only if every
platform built and every check passed — commits the bump, tags it with the upstream version and
publishes a release with the installers.
- **Revisions** of the same upstream release are published by running Upstream release by hand
+1
View File
@@ -0,0 +1 @@
v0.21.2
+12 -1
View File
@@ -19,7 +19,18 @@ WORKTREE="${WORKTREE:-$REPO_ROOT/build/upstream}"
# Branch the patches are applied on top of the upstream tag as.
PATCH_BRANCH=enhanced
PATCH_DIR="$REPO_ROOT/patches"
PATCH_DIR="${PATCH_DIR:-$REPO_ROOT/patches}"
# The upstream release the patches in patches/ were last exported against,
# written there by export-patches.sh. upstream.txt cannot say: a release bumps
# it without exporting the patches again.
patch_base() {
if [ -f "$PATCH_DIR/base.txt" ]; then
tr -d '[:space:]' <"$PATCH_DIR/base.txt"
else
printf '%s\n' "$UPSTREAM_REF"
fi
}
log() {
printf '\033[1;32m==>\033[0m %s\n' "$*"
+3
View File
@@ -26,5 +26,8 @@ git -C "$WORKTREE" format-patch \
--output-directory "$PATCH_DIR" \
"$UPSTREAM_REF..HEAD"
# So that prepare.sh applies them where they fit and rebases from there.
printf '%s\n' "$UPSTREAM_REF" >"$PATCH_DIR/base.txt"
log "patches/ now contains:"
ls -1 "$PATCH_DIR"
+50 -11
View File
@@ -4,11 +4,23 @@
# The result lands in build/upstream on the `enhanced` branch and is what all
# other scripts build from. Running this again always starts from a clean
# upstream tree, so it is safe to repeat.
#
# The patches are applied to the release they were exported against, where they
# always fit, and rebased onto $UPSTREAM_REF when that is a different release.
# A rebase merges against the files the patches were written for, so upstream
# changing something near a patch resolves by itself. `git am` straight onto the
# new release cannot do that: a shallow checkout does not have those files.
. "$(dirname "${BASH_SOURCE[0]}")/common.sh"
BASE_REF="$(patch_base)"
log "Upstream $UPSTREAM_REF from $UPSTREAM_REPO"
fetch_tag() {
git -C "$WORKTREE" fetch --depth 1 --force origin "refs/tags/$1:refs/tags/$1"
}
if [ ! -d "$WORKTREE/.git" ]; then
log "Cloning into $WORKTREE"
mkdir -p "$(dirname "$WORKTREE")"
@@ -16,21 +28,27 @@ if [ ! -d "$WORKTREE/.git" ]; then
else
log "Fetching $UPSTREAM_REF into the existing checkout"
git -C "$WORKTREE" remote set-url origin "$UPSTREAM_REPO"
git -C "$WORKTREE" fetch --depth 1 --force origin "refs/tags/$UPSTREAM_REF:refs/tags/$UPSTREAM_REF"
fetch_tag "$UPSTREAM_REF"
fi
# `git am` refuses to run with a rebase or merge in progress, and a previous
# run may have stopped on a conflict.
git -C "$WORKTREE" am --abort 2>/dev/null || true
if [ "$BASE_REF" != "$UPSTREAM_REF" ]; then
log "Fetching $BASE_REF, which the patches were exported against"
fetch_tag "$BASE_REF"
fi
log "Resetting to $UPSTREAM_REF"
git -C "$WORKTREE" checkout --detach --force "$UPSTREAM_REF"
git -C "$WORKTREE" branch -f "$PATCH_BRANCH" "$UPSTREAM_REF"
# Neither `git am` nor `git rebase` runs while the other is in progress, and a
# previous run may have stopped on a conflict in either.
git -C "$WORKTREE" am --abort 2>/dev/null || true
git -C "$WORKTREE" rebase --abort 2>/dev/null || true
log "Resetting to $BASE_REF"
git -C "$WORKTREE" checkout --detach --force "$BASE_REF"
git -C "$WORKTREE" branch -f "$PATCH_BRANCH" "$BASE_REF"
git -C "$WORKTREE" checkout --force "$PATCH_BRANCH"
git -C "$WORKTREE" reset --hard "$UPSTREAM_REF"
git -C "$WORKTREE" reset --hard "$BASE_REF"
git -C "$WORKTREE" clean -fdx -e node_modules -e target
# `git am` needs an identity for the commits it creates.
# `git am` and `git rebase` need an identity for the commits they create.
git -C "$WORKTREE" config user.name "Modrinth Enhanced"
git -C "$WORKTREE" config user.email "patches@modrinth-enhanced.invalid"
git -C "$WORKTREE" config commit.gpgsign false
@@ -41,11 +59,11 @@ shopt -u nullglob
[ ${#patches[@]} -gt 0 ] || die "No patches found in $PATCH_DIR"
log "Applying ${#patches[@]} patches"
log "Applying ${#patches[@]} patches to $BASE_REF"
if ! git -C "$WORKTREE" am --3way --whitespace=nowarn "${patches[@]}"; then
cat >&2 <<EOF
A patch did not apply to $UPSTREAM_REF.
A patch did not apply to $BASE_REF.
The failed patch is left staged in $WORKTREE so it can be fixed by hand:
@@ -60,5 +78,26 @@ EOF
exit 1
fi
if [ "$BASE_REF" != "$UPSTREAM_REF" ]; then
log "Rebasing the patches onto $UPSTREAM_REF"
if ! git -C "$WORKTREE" rebase --onto "$UPSTREAM_REF" "$BASE_REF" "$PATCH_BRANCH"; then
cat >&2 <<EOF
A patch conflicts with what changed between $BASE_REF and $UPSTREAM_REF.
The rebase is left stopped in $WORKTREE so it can be resolved by hand:
cd $WORKTREE
git status # see the conflicts
# ...resolve them, then:
git add -A && git rebase --continue
# once every patch is in, with upstream.txt set to $UPSTREAM_REF:
$REPO_ROOT/scripts/export-patches.sh
EOF
exit 1
fi
fi
log "Patched checkout ready at $WORKTREE"
git -C "$WORKTREE" --no-pager log --oneline "$UPSTREAM_REF..$PATCH_BRANCH"