feat: add patch series, build scripts and release automation
This commit is contained in:
1 parent
8f4e5e0521
commit
02e18e3765
18 files changed
+23026
No files matched your search
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build Modrinth Enhanced from the patched checkout and collect the installers.
|
||||
#
|
||||
# Expects scripts/prepare.sh to have run. Finished bundles are copied to
|
||||
# build/artifacts.
|
||||
|
||||
. "$(dirname "${BASH_SOURCE[0]}")/common.sh"
|
||||
|
||||
require_worktree
|
||||
|
||||
ARTIFACTS="${ARTIFACTS:-$REPO_ROOT/build/artifacts}"
|
||||
|
||||
# Upstream keeps the API endpoints out of the sources and picks one at build
|
||||
# time; production is the only sensible choice for a release build.
|
||||
log "Selecting the production environment"
|
||||
cp "$WORKTREE/packages/app-lib/.env.prod" "$WORKTREE/packages/app-lib/.env"
|
||||
|
||||
"$REPO_ROOT/scripts/set-version.sh"
|
||||
|
||||
log "Installing JavaScript dependencies"
|
||||
(cd "$WORKTREE" && pnpm install --frozen-lockfile)
|
||||
|
||||
tauri_args=()
|
||||
case "$(uname -s)" in
|
||||
MINGW* | MSYS* | CYGWIN* | Windows_NT)
|
||||
platform=windows
|
||||
tauri_args+=(--bundles nsis)
|
||||
;;
|
||||
Darwin)
|
||||
platform=macos
|
||||
tauri_args+=(--target universal-apple-darwin)
|
||||
;;
|
||||
*)
|
||||
platform=linux
|
||||
;;
|
||||
esac
|
||||
|
||||
log "Building for $platform"
|
||||
(cd "$WORKTREE" && pnpm --filter=@modrinth/app run tauri build "${tauri_args[@]}")
|
||||
|
||||
log "Collecting bundles into $ARTIFACTS"
|
||||
rm -rf "$ARTIFACTS"
|
||||
mkdir -p "$ARTIFACTS"
|
||||
|
||||
if [ "$platform" = macos ]; then
|
||||
bundle_dir="$WORKTREE/target/universal-apple-darwin/release/bundle"
|
||||
else
|
||||
bundle_dir="$WORKTREE/target/release/bundle"
|
||||
fi
|
||||
|
||||
found=0
|
||||
while IFS= read -r -d '' artifact; do
|
||||
cp "$artifact" "$ARTIFACTS/"
|
||||
found=1
|
||||
done < <(find "$bundle_dir" -maxdepth 2 -type f \
|
||||
\( -name '*.AppImage' -o -name '*.deb' -o -name '*.rpm' \
|
||||
-o -name '*.dmg' -o -name '*.app.tar.gz' -o -name '*-setup.exe' \) -print0)
|
||||
|
||||
[ "$found" = 1 ] || die "No bundles were produced under $bundle_dir"
|
||||
|
||||
ls -la "$ARTIFACTS"
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env bash
|
||||
# Verify that the patched checkout still is Modrinth Enhanced.
|
||||
#
|
||||
# A patch can apply cleanly and still stop doing its job - upstream may move
|
||||
# the thing it was holding down. These checks assert the outcome rather than
|
||||
# the diff: the app is named correctly, offline accounts exist, and no
|
||||
# telemetry endpoint survives into the built frontend.
|
||||
#
|
||||
# Checks that need the built frontend are skipped when it is not there yet, so
|
||||
# this is useful both before and after scripts/build.sh.
|
||||
|
||||
. "$(dirname "${BASH_SOURCE[0]}")/common.sh"
|
||||
|
||||
require_worktree
|
||||
|
||||
failures=0
|
||||
|
||||
check() {
|
||||
local description="$1"
|
||||
shift
|
||||
if "$@" >/dev/null 2>&1; then
|
||||
printf ' \033[1;32mok\033[0m %s\n' "$description"
|
||||
else
|
||||
printf ' \033[1;31mFAIL\033[0m %s\n' "$description"
|
||||
failures=$((failures + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
contains() {
|
||||
grep -qF "$2" "$1"
|
||||
}
|
||||
|
||||
missing() {
|
||||
! grep -qrF "$2" "$1"
|
||||
}
|
||||
|
||||
log "Branding"
|
||||
check "tauri.conf.json is named Modrinth Enhanced" \
|
||||
contains "$WORKTREE/apps/app/tauri.conf.json" '"productName": "Modrinth Enhanced"'
|
||||
check "the window is titled Modrinth Enhanced" \
|
||||
contains "$WORKTREE/apps/app/tauri.conf.json" '"title": "Modrinth Enhanced"'
|
||||
check "the icon set was replaced" \
|
||||
test -f "$WORKTREE/apps/app/icons/modrinth-enhanced.svg"
|
||||
|
||||
log "Offline accounts"
|
||||
check "app-lib exposes login_offline" \
|
||||
contains "$WORKTREE/packages/app-lib/src/api/minecraft_auth.rs" 'pub async fn login_offline'
|
||||
check "the Tauri command is registered" \
|
||||
contains "$WORKTREE/apps/app/src/api/auth.rs" 'login_offline,'
|
||||
check "the frontend can reach it" \
|
||||
contains "$WORKTREE/apps/app-frontend/src/helpers/auth.js" "plugin:auth|login_offline"
|
||||
|
||||
log "No telemetry in the sources"
|
||||
# Quoted, so that the module names being mentioned in a comment explaining why
|
||||
# they are gone does not count as importing them.
|
||||
check "nothing imports posthog-js" \
|
||||
missing "$WORKTREE/apps/app-frontend/src" "'posthog-js'"
|
||||
check "nothing imports @sentry/vue" \
|
||||
missing "$WORKTREE/apps/app-frontend/src" "'@sentry/vue'"
|
||||
check "the Tally embed is gone" \
|
||||
missing "$WORKTREE/apps/app-frontend/index.html" "tally.so"
|
||||
check "playtime is not reported" \
|
||||
missing "$WORKTREE/packages/app-lib/src/api/instance/run.rs" "analytics/playtime"
|
||||
check "server plays are not reported" \
|
||||
missing "$WORKTREE/packages/app-lib/src/api/instance/run.rs" "analytics/minecraft-server-play"
|
||||
check "the CSP does not allow PostHog" \
|
||||
missing "$WORKTREE/apps/app/tauri.conf.json" "posthog"
|
||||
check "the CSP does not allow Sentry" \
|
||||
missing "$WORKTREE/apps/app/tauri.conf.json" "sentry.io"
|
||||
|
||||
dist="$WORKTREE/apps/app-frontend/dist"
|
||||
if [ -d "$dist" ]; then
|
||||
log "No telemetry in the built frontend"
|
||||
check "no PostHog endpoint in the bundle" missing "$dist" "posthog.modrinth.com"
|
||||
check "no Sentry endpoint in the bundle" missing "$dist" "ingest.us.sentry.io"
|
||||
check "no Tally embed in the bundle" missing "$dist" "tally.so"
|
||||
else
|
||||
warn "Skipping bundle checks: $dist does not exist yet"
|
||||
fi
|
||||
|
||||
artifacts="${ARTIFACTS:-$REPO_ROOT/build/artifacts}"
|
||||
if [ -d "$artifacts" ] && [ -n "$(ls -A "$artifacts" 2>/dev/null)" ]; then
|
||||
log "Installers"
|
||||
for artifact in "$artifacts"/*; do
|
||||
name="$(basename "$artifact")"
|
||||
case "$name" in
|
||||
"Modrinth Enhanced"* | ModrinthEnhanced* | "Modrinth_Enhanced"*)
|
||||
printf ' \033[1;32mok\033[0m %s\n' "$name"
|
||||
;;
|
||||
*)
|
||||
printf ' \033[1;31mFAIL\033[0m %s is not named after Modrinth Enhanced\n' "$name"
|
||||
failures=$((failures + 1))
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$failures" -gt 0 ]; then
|
||||
die "$failures check(s) failed"
|
||||
fi
|
||||
|
||||
log "All checks passed"
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# Shared settings for every script in this repository.
|
||||
#
|
||||
# Source this, do not run it.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
|
||||
# Where the Modrinth App sources come from, and which release of them the
|
||||
# patches in this repository are written against.
|
||||
UPSTREAM_REPO="${UPSTREAM_REPO:-https://github.com/modrinth/code.git}"
|
||||
UPSTREAM_REF="${UPSTREAM_REF:-$(tr -d '[:space:]' <"$REPO_ROOT/upstream.txt")}"
|
||||
|
||||
# The patched checkout. Everything in here is disposable: it is recreated from
|
||||
# the upstream tag plus patches/ and is never committed to this repository.
|
||||
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"
|
||||
|
||||
log() {
|
||||
printf '\033[1;32m==>\033[0m %s\n' "$*"
|
||||
}
|
||||
|
||||
warn() {
|
||||
printf '\033[1;33m==>\033[0m %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
die() {
|
||||
printf '\033[1;31m==>\033[0m %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Version the built app reports, derived from the upstream tag it is built
|
||||
# from: `v0.20.5` becomes `0.20.5`.
|
||||
app_version() {
|
||||
printf '%s\n' "${UPSTREAM_REF#v}"
|
||||
}
|
||||
|
||||
require_worktree() {
|
||||
[ -d "$WORKTREE/.git" ] || die "No patched checkout at $WORKTREE. Run scripts/prepare.sh first."
|
||||
}
|
||||
Executable
+30
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
# Regenerate patches/ from the commits in build/upstream.
|
||||
#
|
||||
# Run this after changing anything in the patched checkout: every commit on top
|
||||
# of the upstream tag becomes one patch file, and patch files that no longer
|
||||
# have a commit behind them are deleted.
|
||||
|
||||
. "$(dirname "${BASH_SOURCE[0]}")/common.sh"
|
||||
|
||||
require_worktree
|
||||
|
||||
if [ -n "$(git -C "$WORKTREE" status --porcelain)" ]; then
|
||||
die "$WORKTREE has uncommitted changes. Commit them first: each commit becomes one patch."
|
||||
fi
|
||||
|
||||
count="$(git -C "$WORKTREE" rev-list --count "$UPSTREAM_REF..HEAD")"
|
||||
[ "$count" -gt 0 ] || die "No commits on top of $UPSTREAM_REF to export."
|
||||
|
||||
log "Exporting $count patches"
|
||||
rm -f "$PATCH_DIR"/*.patch
|
||||
git -C "$WORKTREE" format-patch \
|
||||
--binary \
|
||||
--zero-commit \
|
||||
--no-signature \
|
||||
--no-numbered \
|
||||
--output-directory "$PATCH_DIR" \
|
||||
"$UPSTREAM_REF..HEAD"
|
||||
|
||||
log "patches/ now contains:"
|
||||
ls -1 "$PATCH_DIR"
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# Print the newest Modrinth App release tag upstream has published.
|
||||
#
|
||||
# With --write, also store it in upstream.txt. Exits with status 0 either way;
|
||||
# compare the output with upstream.txt to find out whether anything moved.
|
||||
|
||||
. "$(dirname "${BASH_SOURCE[0]}")/common.sh"
|
||||
|
||||
latest="$(
|
||||
git ls-remote --tags --refs "$UPSTREAM_REPO" 'v*' |
|
||||
sed 's#.*refs/tags/##' |
|
||||
grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' |
|
||||
sort -V |
|
||||
tail -1
|
||||
)"
|
||||
|
||||
[ -n "$latest" ] || die "Could not determine the latest upstream release tag"
|
||||
|
||||
if [ "${1:-}" = --write ]; then
|
||||
printf '%s\n' "$latest" >"$REPO_ROOT/upstream.txt"
|
||||
fi
|
||||
|
||||
printf '%s\n' "$latest"
|
||||
Executable
+64
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env bash
|
||||
# Check out the pinned Modrinth App release and apply every patch on top of it.
|
||||
#
|
||||
# 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.
|
||||
|
||||
. "$(dirname "${BASH_SOURCE[0]}")/common.sh"
|
||||
|
||||
log "Upstream $UPSTREAM_REF from $UPSTREAM_REPO"
|
||||
|
||||
if [ ! -d "$WORKTREE/.git" ]; then
|
||||
log "Cloning into $WORKTREE"
|
||||
mkdir -p "$(dirname "$WORKTREE")"
|
||||
git clone --depth 1 --branch "$UPSTREAM_REF" "$UPSTREAM_REPO" "$WORKTREE"
|
||||
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"
|
||||
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
|
||||
|
||||
log "Resetting to $UPSTREAM_REF"
|
||||
git -C "$WORKTREE" checkout --detach --force "$UPSTREAM_REF"
|
||||
git -C "$WORKTREE" branch -f "$PATCH_BRANCH" "$UPSTREAM_REF"
|
||||
git -C "$WORKTREE" checkout --force "$PATCH_BRANCH"
|
||||
git -C "$WORKTREE" reset --hard "$UPSTREAM_REF"
|
||||
git -C "$WORKTREE" clean -fdx -e node_modules -e target
|
||||
|
||||
# `git am` needs an identity for the commits it creates.
|
||||
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
|
||||
|
||||
shopt -s nullglob
|
||||
patches=("$PATCH_DIR"/*.patch)
|
||||
shopt -u nullglob
|
||||
|
||||
[ ${#patches[@]} -gt 0 ] || die "No patches found in $PATCH_DIR"
|
||||
|
||||
log "Applying ${#patches[@]} patches"
|
||||
if ! git -C "$WORKTREE" am --3way --whitespace=nowarn "${patches[@]}"; then
|
||||
cat >&2 <<EOF
|
||||
|
||||
A patch did not apply to $UPSTREAM_REF.
|
||||
|
||||
The failed patch is left staged in $WORKTREE so it can be fixed by hand:
|
||||
|
||||
cd $WORKTREE
|
||||
git status # see the conflicts
|
||||
# ...resolve them, then:
|
||||
git add -A && git am --continue
|
||||
# once every patch is in:
|
||||
$REPO_ROOT/scripts/export-patches.sh
|
||||
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Patched checkout ready at $WORKTREE"
|
||||
git -C "$WORKTREE" --no-pager log --oneline "$UPSTREAM_REF..$PATCH_BRANCH"
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
# Stamp the build version into the patched checkout.
|
||||
#
|
||||
# Upstream leaves `1.0.0-local` in the sources and injects the real version in
|
||||
# its release workflow; this does the same. Without an argument the version is
|
||||
# derived from the upstream tag in upstream.txt.
|
||||
|
||||
. "$(dirname "${BASH_SOURCE[0]}")/common.sh"
|
||||
|
||||
require_worktree
|
||||
|
||||
version="${1:-$(app_version)}"
|
||||
version="${version#v}"
|
||||
|
||||
log "Setting version to $version"
|
||||
|
||||
python3 - "$WORKTREE" "$version" <<'PY'
|
||||
import json
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
worktree = pathlib.Path(sys.argv[1])
|
||||
version = sys.argv[2]
|
||||
|
||||
for relative in ("apps/app/Cargo.toml", "packages/app-lib/Cargo.toml"):
|
||||
path = worktree / relative
|
||||
text = path.read_text(encoding="utf-8")
|
||||
# Only the `version` key of the leading [package] table, never a dependency.
|
||||
patched, count = re.subn(
|
||||
r'(?m)^version = "[^"]*"$', f'version = "{version}"', text, count=1
|
||||
)
|
||||
if count != 1:
|
||||
sys.exit(f"Could not find a package version in {relative}")
|
||||
path.write_text(patched, encoding="utf-8")
|
||||
print(f" {relative}")
|
||||
|
||||
path = worktree / "apps/app-frontend/package.json"
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
data["version"] = version
|
||||
# Keep the file byte-compatible with the tab indentation upstream uses so that
|
||||
# `prettier --check` stays happy.
|
||||
path.write_text(json.dumps(data, indent="\t", ensure_ascii=False) + "\n", encoding="utf-8")
|
||||
print(" apps/app-frontend/package.json")
|
||||
PY
|
||||
Reference in new issue
Block a user