#!/bin/bash # PhotoGIMP on a real Mac: installs GIMP 3.2.6 from the official DMG, applies PhotoGIMP the README # way (Terminal rsync), our repair and upstream master, then uninstalls. Facts and screenshots go to # verify/$VERIFY_OUT/. set -u cd "$(dirname "$0")/.." REPO=$(pwd) OUT="$REPO/verify/${VERIFY_OUT:-results-macos}"; mkdir -p "$OUT" LOG="$OUT/run-log.txt"; FACTS="$OUT/facts.txt"; : > "$FACTS" # Second run (audit follow-up): migration, pasted commands in zsh, normal quits, refusal while running (pgrep -x name match). log() { echo "[$(date +%H:%M:%S)] $*" | tee -a "$LOG"; } fact() { printf '%s\t%s\n' "$1" "$2" >> "$FACTS"; } shot() { if screencapture -x "$OUT/$1.png" 2>>"$LOG"; then log "screenshot $1"; else log "screenshot $1 FAILED"; fi; } sha() { shasum -a 256 "$1" 2>/dev/null | cut -d' ' -f1; } GIMP_URL=https://download.gimp.org/gimp/v3.2/macos/gimp-3.2.6-arm64.dmg GIMP_SHA=854573aec2be6a185aa021109c3c0ca94f210379204b704231bdd191b4060294 [ "$(uname -m)" = x86_64 ] && GIMP_URL=https://download.gimp.org/gimp/v3.2/macos/gimp-3.2.6-x86_64.dmg && GIMP_SHA=ebe633642445d4d6fc6b68e7be7ca581c0b28c18dc9de7747849528c982e5677 PG_URL=https://github.com/Diolinux/PhotoGIMP/releases/download/3.1/PhotoGIMP.zip PG_SHA=de6a17ffb70000051372d84141b7e69fc2e60a0af3bd3b3100c9afd5e0ff3ae6 MASTER=fc61a8f IMG="$REPO/assets/moraine-lake.jpg" CFG="$HOME/Library/Application Support/GIMP" D32="$CFG/3.2" brew install cliclick >/dev/null 2>&1 || true # Keep the runner's native resolution: after switching to 1920x1080 GIMP's window stopped drawing (run 2). fact os "$(sw_vers -productName) $(sw_vers -productVersion) ($(sw_vers -buildVersion))" fact arch "$(uname -m)" fact date "$(date '+%Y-%m-%d %H:%M')" BIN="" dismiss_network_prompt() { # macOS asks once per app: "Allow GIMP to find devices on local networks?" We answer Don't Allow. for i in 1 2 3; do for proc in UserNotificationCenter CoreServicesUIAgent SecurityAgent universalAccessAuthWarn; do osascript -e "tell application \"System Events\" to tell process \"$proc\" to click button \"Don’t Allow\" of window 1" >/dev/null 2>&1 && { log "network prompt answered via $proc"; fact network_prompt_owner "$proc"; return; } osascript -e "tell application \"System Events\" to tell process \"$proc\" to click button \"Don't Allow\" of window 1" >/dev/null 2>&1 && { log "network prompt answered via $proc"; fact network_prompt_owner "$proc"; return; } done if command -v cliclick >/dev/null; then cliclick c:453,322 && log "clicked Don't Allow at 453,322"; fi sleep 2 done } close_welcome() { osascript -e 'tell application "System Events"' -e 'set p to first process whose unix id is '"$GPID" -e 'set frontmost of p to true' -e 'try' -e 'perform action "AXRaise" of (first window of p whose name starts with "Welcome")' -e 'end try' -e 'end tell' 2>>"$LOG" sleep 1 osascript -e 'tell application "System Events" to key code 53' 2>/dev/null; sleep 1.5 if osascript -e 'tell application "System Events" to exists (first window of (first process whose unix id is '"$GPID"') whose name starts with "Welcome")' 2>/dev/null | grep -q true; then osascript -e 'tell application "System Events" to keystroke "w" using {command down}' 2>/dev/null; sleep 1.5 log "welcome: tried Cmd+W" fi osascript -e 'tell application "System Events" to exists (first window of (first process whose unix id is '"$GPID"') whose name starts with "Welcome")' 2>/dev/null | grep -q true && log "welcome still open" || log "welcome closed" } start_gimp() { # tag, with-image(0/1), settle, splash(0/1) local tag=$1 img=$2 settle=${3:-25} sp=${4:-0} if [ "$img" = 1 ]; then "$BIN" --console-messages "$IMG" >"$OUT/$tag-stdout.txt" 2>"$OUT/$tag-stderr.txt" & else "$BIN" --console-messages >"$OUT/$tag-stdout.txt" 2>"$OUT/$tag-stderr.txt" & fi GPID=$! [ "$sp" = 1 ] && splash_shots "$tag" local t=0 while [ $t -lt 240 ]; do kill -0 $GPID 2>/dev/null || { log "$tag GIMP exited early"; break; } [ -d "$D32" ] && [ $t -ge 15 ] && break sleep 1; t=$((t+1)) done sleep "$settle" [ "${KEEP_WELCOME:-0}" = 1 ] || close_welcome osascript -e 'tell application "System Events" to set frontmost of (first process whose unix id is '"$GPID"') to true' 2>/dev/null sleep 2 } splash_shots() { local tag=$1; for s in 2 4 6 9; do sleep 2; shot "$tag-splash-$s"; done; } # Quit like a user: Cmd+Q (an unchanged image does not ask anything). Falls back to TERM only if GIMP hangs. quit_gimp() { osascript -e 'tell application "System Events" to set frontmost of (first process whose unix id is '"$GPID"') to true' 2>/dev/null; sleep 1 osascript -e 'tell application "System Events" to keystroke "q" using {command down}' 2>/dev/null for i in $(seq 1 30); do kill -0 $GPID 2>/dev/null || { log "quit gracefully"; return 0; }; sleep 1; done log "Cmd+Q did not quit GIMP; stopping it"; stop_gimp; return 1 } # Bring GIMP's main window into the 1024x768 screen, as a user would by dragging it. fit_window() { osascript -e 'tell application "System Events" to tell (first process whose unix id is '"$GPID"')' -e 'set position of window 1 to {0, 25}' -e 'set size of window 1 to {1024, 743}' -e 'end tell' 2>>"$LOG"; sleep 3 } stop_gimp() { kill -TERM $GPID 2>/dev/null for i in $(seq 1 20); do kill -0 $GPID 2>/dev/null || break; sleep 1; done kill -9 $GPID 2>/dev/null; pkill -9 -f 'GIMP.app/Contents/MacOS' 2>/dev/null; sleep 2 } no_welcome() { grep -q show-welcome-dialog "$1/gimprc" 2>/dev/null || printf '\n(show-welcome-dialog no)\n' >> "$1/gimprc"; } snap() { # tag fact "config_$1_files" "$(ls "$D32" 2>/dev/null | tr '\n' ' ')" fact "config_$1_toolrc_sha" "$(sha "$D32/toolrc")" fact "config_$1_toolrc_has_vector" "$(grep -c gimp-vector-tool "$D32/toolrc" 2>/dev/null)" [ -f "$D32/toolrc" ] && cp "$D32/toolrc" "$OUT/$1-toolrc.txt" fact "config_$1_appsupport_gimp" "$(ls "$CFG" 2>/dev/null | tr '\n' ' ')" } repair() { sed -i '' 's/"gimp-vector-tool"/"gimp-path-tool"/' "$1/toolrc" sed -i '' 's/"gimp-vectors-list"/"gimp-path-list"/' "$1/sessionrc" sed -i '' 's/"tools-vector"/"tools-path"/' "$1/shortcutsrc" } reset_stock() { rm -rf "$D32" "$CFG/3.0"; start_gimp reset 0 10; stop_gimp; no_welcome "$D32"; } log '===== download and install GIMP' curl -sSL -o /tmp/gimp.dmg "$GIMP_URL" fact gimp_dmg_sha256 "$(sha /tmp/gimp.dmg)"; [ "$(sha /tmp/gimp.dmg)" = "$GIMP_SHA" ] && fact gimp_dmg_sha_ok yes || fact gimp_dmg_sha_ok NO MNT=$(hdiutil attach -nobrowse -noautoopen /tmp/gimp.dmg | tail -1 | cut -f3-) fact dmg_contents "$(ls "$MNT" | tr '\n' ' ')" APP=$(ls -d "$MNT"/*.app | head -1) cp -R "$APP" /Applications/ hdiutil detach "$MNT" -quiet APPN="/Applications/$(basename "$APP")" fact app "$APPN" fact app_version "$(defaults read "$APPN/Contents/Info.plist" CFBundleShortVersionString 2>/dev/null)" fact app_macos_dir "$(ls "$APPN/Contents/MacOS" | tr '\n' ' ')" BIN="$APPN/Contents/MacOS/$(ls "$APPN/Contents/MacOS" | grep -i '^gimp' | head -1)" fact bin "$BIN" fact quarantine "$(xattr -l "$APPN" 2>/dev/null | head -3 | tr '\n' ' ')" log '===== first run' fact appsupport_before "$(ls "$CFG" 2>/dev/null | tr '\n' ' ')" KEEP_WELCOME=1 start_gimp 01-first-run 0 25; shot 01-first-run-prompt; dismiss_network_prompt; sleep 2; shot 01-first-run; stop_gimp fact appsupport_after_first_run "$(ls "$CFG" 2>/dev/null | tr '\n' ' ')" snap stock no_welcome "$D32" log '===== Finder at ~/Library/Application Support/GIMP' open "$CFG"; sleep 4; shot 02-finder-gimp-folder osascript -e 'tell application "Finder" to activate' -e 'tell application "System Events" to keystroke "g" using {command down, shift down}' 2>>"$LOG" && sleep 2 && shot 03-go-to-folder osascript -e 'tell application "System Events" to key code 53' 2>/dev/null osascript -e 'tell application "Finder" to close every window' 2>/dev/null log '===== download PhotoGIMP 3.1' curl -sSL -o "$HOME/Desktop/PhotoGIMP.zip" "$PG_URL" fact photogimp_zip_sha256 "$(sha "$HOME/Desktop/PhotoGIMP.zip")" [ "$(sha "$HOME/Desktop/PhotoGIMP.zip")" = "$PG_SHA" ] && fact photogimp_zip_sha_ok yes || fact photogimp_zip_sha_ok NO mkdir -p "$HOME/Desktop/PhotoGIMP" && ditto -x -k "$HOME/Desktop/PhotoGIMP.zip" "$HOME/Desktop/PhotoGIMP" SRC="$HOME/Desktop/PhotoGIMP/3.0" fact zip_top "$(ls -a "$HOME/Desktop/PhotoGIMP" | tr '\n' ' ')" open "$HOME/Desktop/PhotoGIMP"; sleep 3; shot 04-extracted-zip osascript -e 'tell application "Finder" to close every window' 2>/dev/null log '===== A: 3.0 folder next to 3.2' cp -R "$SRC" "$CFG/3.0" start_gimp 05-A-wrong-folder 1; shot 05-A-wrong-folder; stop_gimp snap A; rm -rf "$CFG/3.0" log '===== B: README Terminal method (rsync into 3.2)' rsync -av --ignore-times "$SRC/" "$D32/" > "$OUT/B-rsync.txt" 2>&1 snap B_before_launch KEEP_WELCOME=1 start_gimp 06-B-welcome 1 15 1; shot 06-B-welcome; stop_gimp snap B_after_first_quit no_welcome "$D32" start_gimp 07-B-photogimp31-on-326 1; shot 07-B-photogimp31-on-326; stop_gimp snap B_after_second_quit log '===== C: our repair' reset_stock rsync -a "$SRC/" "$D32/"; repair "$D32"; no_welcome "$D32" start_gimp 08-C-repaired 1 25 1; shot 08-C-repaired; stop_gimp snap C_after_quit start_gimp 09-C-second-start 1; shot 09-C-second-start; stop_gimp log '===== D: upstream master' reset_stock curl -sSL -o /tmp/master.zip "https://github.com/Diolinux/PhotoGIMP/archive/$MASTER.zip" rm -rf /tmp/pgm && mkdir /tmp/pgm && ditto -x -k /tmp/master.zip /tmp/pgm M30=$(find /tmp/pgm -type d -path '*/.config/GIMP/3.0' | head -1) rsync -a "$M30/" "$D32/"; no_welcome "$D32" start_gimp 10-D-master 1; shot 10-D-master; stop_gimp snap D_after_quit # Paste a path-finder command into an interactive zsh (macOS's default shell), the way a user does. paste_zsh() { zsh -i -c "$(cat "$REPO/verify/site/$1")" > "$OUT/paste-$1.txt" 2>&1 "$OUT/migrate-warnings.txt" 2>/dev/null; fact migrate_warnings "$(wc -l < "$OUT/migrate-warnings.txt" | tr -d ' ')" log '===== S: path finder install (pasted into zsh), two starts with normal quits' reset_stock paste_zsh mac-install-release-32.sh; snap paste_installed start_gimp 22-paste-installed 1; fit_window; shot 22-paste-installed; quit_gimp; fact paste_quit_graceful "$?"; snap paste_after_quit start_gimp 23-paste-second 1; fit_window; shot 23-paste-second; quit_gimp; snap paste_after_second_quit grep -h "toolrc\|not existing\|no entry" "$OUT/22-paste-installed-stderr.txt" > "$OUT/paste-warnings.txt" 2>/dev/null; fact paste_warnings "$(wc -l < "$OUT/paste-warnings.txt" | tr -d ' ')" log '===== R: refuses while GIMP runs' before=$(ls "$CFG" | tr '\n' ' ') start_gimp 24-running 0 15; paste_zsh mac-install-release-32.sh; fact running_folders_unchanged "$([ "$(ls "$CFG" | tr '\n' ' ')" = "$before" ] && echo yes || echo NO)"; quit_gimp log '===== U: remove PhotoGIMP only, restore backup, master, repair, reset (all pasted into zsh)' paste_zsh mac-uninstall-files-32.sh; snap paste_uninstalled paste_zsh mac-uninstall-backup-32.sh; snap paste_restored paste_zsh mac-install-master-32.sh; snap paste_master start_gimp 25-paste-master 1; fit_window; shot 25-paste-master; quit_gimp; snap paste_master_after_quit grep -h "toolrc\|not existing\|no entry\|duplicate accel" "$OUT/25-paste-master-stderr.txt" > "$OUT/master-messages.txt" 2>/dev/null rm -rf "$D32"; start_gimp reset2 0 10; quit_gimp; cp -R /tmp/pg31/3.0/. "$D32/"; paste_zsh mac-repair-32.sh; snap paste_repaired paste_zsh mac-uninstall-all-32.sh; fact appsupport_after_reset "$(ls "$CFG" | tr '\n' ' ')" python3 - "$FACTS" "$OUT/facts.json" <<'EOF2' import json, sys d = {} for line in open(sys.argv[1], encoding='utf-8', errors='replace'): k, _, v = line.rstrip('\n').partition('\t'); d[k] = v json.dump(d, open(sys.argv[2], 'w'), indent=1) EOF2 log done