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 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 Patches are applied to the release they were exported against (`patches/base.txt`), where they
themselves. When one genuinely conflicts, `scripts/prepare.sh` stops and leaves the conflict staged always fit, and then rebased onto the release in `upstream.txt` when that is newer. The rebase
in `build/upstream` to be resolved with `git am --continue`, after which `scripts/export-patches.sh` merges against the files the patches were written for, so upstream changes near a hunk resolve by
writes the fixed series back. 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: 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 reusable workflow the release job calls. It applies the patches, checks them, and builds on
Linux, Windows and macOS. Linux, Windows and macOS.
- **Upstream release** (`.github/workflows/upstream-release.yml`) runs daily. If Modrinth has - **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 platform built and every check passed — commits the bump, tags it with the upstream version and
publishes a release with the installers. publishes a release with the installers.
- **Revisions** of the same upstream release are published by running Upstream release by hand - **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. # Branch the patches are applied on top of the upstream tag as.
PATCH_BRANCH=enhanced 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() { log() {
printf '\033[1;32m==>\033[0m %s\n' "$*" printf '\033[1;32m==>\033[0m %s\n' "$*"
+3
View File
@@ -26,5 +26,8 @@ git -C "$WORKTREE" format-patch \
--output-directory "$PATCH_DIR" \ --output-directory "$PATCH_DIR" \
"$UPSTREAM_REF..HEAD" "$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:" log "patches/ now contains:"
ls -1 "$PATCH_DIR" 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 # 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 # other scripts build from. Running this again always starts from a clean
# upstream tree, so it is safe to repeat. # 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" . "$(dirname "${BASH_SOURCE[0]}")/common.sh"
BASE_REF="$(patch_base)"
log "Upstream $UPSTREAM_REF from $UPSTREAM_REPO" 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 if [ ! -d "$WORKTREE/.git" ]; then
log "Cloning into $WORKTREE" log "Cloning into $WORKTREE"
mkdir -p "$(dirname "$WORKTREE")" mkdir -p "$(dirname "$WORKTREE")"
@@ -16,21 +28,27 @@ if [ ! -d "$WORKTREE/.git" ]; then
else else
log "Fetching $UPSTREAM_REF into the existing checkout" log "Fetching $UPSTREAM_REF into the existing checkout"
git -C "$WORKTREE" remote set-url origin "$UPSTREAM_REPO" 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 fi
# `git am` refuses to run with a rebase or merge in progress, and a previous if [ "$BASE_REF" != "$UPSTREAM_REF" ]; then
# run may have stopped on a conflict. log "Fetching $BASE_REF, which the patches were exported against"
git -C "$WORKTREE" am --abort 2>/dev/null || true fetch_tag "$BASE_REF"
fi
log "Resetting to $UPSTREAM_REF" # Neither `git am` nor `git rebase` runs while the other is in progress, and a
git -C "$WORKTREE" checkout --detach --force "$UPSTREAM_REF" # previous run may have stopped on a conflict in either.
git -C "$WORKTREE" branch -f "$PATCH_BRANCH" "$UPSTREAM_REF" 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" 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 -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.name "Modrinth Enhanced"
git -C "$WORKTREE" config user.email "patches@modrinth-enhanced.invalid" git -C "$WORKTREE" config user.email "patches@modrinth-enhanced.invalid"
git -C "$WORKTREE" config commit.gpgsign false git -C "$WORKTREE" config commit.gpgsign false
@@ -41,11 +59,11 @@ shopt -u nullglob
[ ${#patches[@]} -gt 0 ] || die "No patches found in $PATCH_DIR" [ ${#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 if ! git -C "$WORKTREE" am --3way --whitespace=nowarn "${patches[@]}"; then
cat >&2 <<EOF 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: The failed patch is left staged in $WORKTREE so it can be fixed by hand:
@@ -60,5 +78,26 @@ EOF
exit 1 exit 1
fi 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" log "Patched checkout ready at $WORKTREE"
git -C "$WORKTREE" --no-pager log --oneline "$UPSTREAM_REF..$PATCH_BRANCH" git -C "$WORKTREE" --no-pager log --oneline "$UPSTREAM_REF..$PATCH_BRANCH"