#!/usr/bin/env bash # update-wordpress.sh — move the fork onto a new WordPress release. # # tools/update-wordpress.sh 7.1.2 # tools/update-wordpress.sh 7.1.2 --refresh # also rewrite the patch series # tools/update-wordpress.sh 7.1.2 --diff wp-admin/install.php # # Fetches the release, verifies it against the SHA-1 wordpress.org publishes, # applies the patch series, and reports whether the fork still fits. # # Nothing is committed and tools/wordpress.lock is only rewritten once the # series has actually applied — so a failed run leaves the fork building the # last known-good version rather than a broken one. set -euo pipefail here="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$here" VERSION="${1:-}" REFRESH=0 DIFF_FILE="" shift || true while [ $# -gt 0 ]; do case "$1" in --refresh) REFRESH=1 ;; --diff) shift; DIFF_FILE="${1:-}" ;; *) echo "unknown option: $1" >&2; exit 2 ;; esac shift done say() { printf '\033[1m→\033[0m %s\n' "$*"; } warn() { printf '\033[33mwarning:\033[0m %s\n' "$*" >&2; } die() { printf '\033[31merror:\033[0m %s\n' "$*" >&2; exit 1; } [ -n "$VERSION" ] || die "usage: tools/update-wordpress.sh [--refresh] [--diff ]" [[ "$VERSION" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]] || die "'$VERSION' does not look like a WordPress version" # shellcheck source=wordpress.lock source tools/wordpress.lock CURRENT="$WP_VERSION" WORK="$here/dist/.upstream" CACHE="$here/dist/.cache" mkdir -p "$WORK" "$CACHE" URL="https://wordpress.org/wordpress-${VERSION}.tar.gz" TARBALL="$CACHE/wordpress-${VERSION}.tar.gz" # ------------------------------------------------------------------- fetch if [ ! -f "$TARBALL" ]; then say "downloading WordPress $VERSION" curl -fsSL -o "$TARBALL.part" "$URL" || die "could not download $URL — does that version exist?" mv "$TARBALL.part" "$TARBALL" fi say "verifying against wordpress.org's published SHA-1" published="$(curl -fsSL "${URL}.sha1" 2>/dev/null || true)" actual_sha1="$(sha1sum "$TARBALL" | cut -d' ' -f1)" if [ -z "$published" ]; then warn "wordpress.org did not serve a .sha1 for this release; continuing on the SHA-256 recorded below" elif [ "$published" != "$actual_sha1" ]; then rm -f "$TARBALL" die "SHA-1 mismatch — the download does not match what wordpress.org publishes. published $published got $actual_sha1" fi NEW_SHA256="$(sha256sum "$TARBALL" | cut -d' ' -f1)" # ------------------------------------------------------------------ unpack say "unpacking" rm -rf "${WORK:?}/new" "${WORK:?}/pristine" "${WORK:?}/wordpress" tar -xzf "$TARBALL" -C "$WORK" [ -d "$WORK/wordpress" ] || die "the tarball did not contain a wordpress/ directory" mv "$WORK/wordpress" "$WORK/new" cp -R "$WORK/new" "$WORK/pristine" # --------------------------------------------------------- what changed? if [ -n "$DIFF_FILE" ]; then old_tarball="$CACHE/wordpress-${CURRENT}.tar.gz" if [ ! -f "$old_tarball" ]; then say "fetching $CURRENT for comparison" curl -fsSL -o "$old_tarball" "$WP_URL" fi rm -rf "$WORK/old" mkdir -p "$WORK/old" tar -xzf "$old_tarball" -C "$WORK/old" say "upstream's own changes to $DIFF_FILE between $CURRENT and $VERSION:" diff -u "$WORK/old/wordpress/$DIFF_FILE" "$WORK/new/$DIFF_FILE" || true echo fi # ------------------------------------------------------------ the series shopt -s nullglob patches=(patches/*.patch) shopt -u nullglob failed=0 fuzzed=0 if [ ${#patches[@]} -eq 0 ]; then say "no patches to apply" else say "applying ${#patches[@]} patch(es) to WordPress $VERSION" for patch in "${patches[@]}"; do name="$(basename "$patch")" output="$(cd "$WORK/new" && patch -p1 -F3 --forward < "$here/$patch" 2>&1)" && rc=0 || rc=$? if [ "$rc" -ne 0 ]; then echo " FAILED $name" echo "$output" | sed 's/^/ /' failed=1 elif echo "$output" | grep -q 'with fuzz'; then echo " fuzz $name" echo "$output" | grep 'with fuzz' | sed 's/^/ /' fuzzed=1 else echo " ok $name" fi done fi echo if [ "$failed" -eq 1 ]; then cat >&2 < dist/.upstream/new/ 3. Replace the hunks in the failing patch with the result, keeping its Subject: header and explanation. 4. Run this script again. EOF exit 1 fi # ------------------------------------------------------------ refresh if [ "$REFRESH" -eq 1 ]; then say "regenerating the patch series against $VERSION" for patch in "${patches[@]}"; do file="$(grep -m1 '^--- a/' "$patch" | sed 's|^--- a/||')" [ -n "$file" ] || { warn "$(basename "$patch") has no file header; left alone"; continue; } header="$(sed -n '1,/^--- a\//p' "$patch" | sed '$d')" { echo "$header" | sed "s/^Applies to: WordPress .*/Applies to: WordPress $VERSION/" diff -u "$WORK/pristine/$file" "$WORK/new/$file" \ | sed -e "1s|.*|--- a/$file|" -e "2s|.*|+++ b/$file|" } > "$patch.new" mv "$patch.new" "$patch" echo " refreshed $(basename "$patch")" done elif [ "$fuzzed" -eq 1 ]; then warn "the series applied with fuzz. Run again with --refresh so the next release starts clean." fi # --------------------------------------------------------------- the lock say "updating tools/wordpress.lock" python3 - "$VERSION" "$URL" "$NEW_SHA256" "$actual_sha1" <<'PY' import io, sys version, url, sha256, sha1 = sys.argv[1:5] path = 'tools/wordpress.lock' out = [] for line in io.open(path, encoding='utf-8'): if line.startswith('WP_VERSION='): out.append(f'WP_VERSION={version}\n') elif line.startswith('WP_URL='): out.append(f'WP_URL={url}\n') elif line.startswith('WP_SHA256='): out.append(f'WP_SHA256={sha256}\n') elif line.startswith('WP_SHA1='): out.append(f'WP_SHA1={sha1}\n') else: out.append(line) io.open(path, 'w', encoding='utf-8', newline='').write(''.join(out)) PY # The compose defaults mirror the lock so a plain `docker compose build` # cannot drift onto a different core. sed -i \ -e "s|WP_VERSION: \${WP_VERSION:-.*}|WP_VERSION: \${WP_VERSION:-$VERSION}|" \ -e "s|WP_URL: \${WP_URL:-.*}|WP_URL: \${WP_URL:-$URL}|" \ -e "s|WP_SHA256: \${WP_SHA256:-.*}|WP_SHA256: \${WP_SHA256:-$NEW_SHA256}|" \ docker/docker-compose.yml cat < $VERSION docker/docker-compose.yml build args updated Next: tests/run.sh tools/build.sh then walk through wp-admin/install.php once — the setup wizard is the only patched file, so it is the only thing this can have broken. EOF