#!/usr/bin/env bash # prune-data.sh deletes from the MegaMek tree at image build. Two ways that goes # wrong are invisible until a match runs, and both are what the guards exist for: # # - a MegaMek bump renames what a prune line names, the glob matches nothing, # and the build quietly ships the bytes the line was written to remove # - a glob grows too broad and takes a file the game needs with it # # So the test is not "does rm work". It is that both failures stop the build. set -uo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" TMP="$(mktemp -d)" trap 'rm -rf "$TMP"' EXIT pass=0; fail=0 check() { if "${@:2}"; then echo "ok $1"; pass=$((pass+1)); else echo "FAIL $1"; fail=$((fail+1)); fi; } # A MegaMek tree carrying one file per path the script names, plus one file it # must not touch in each directory it prunes from. stage_tree() { # local mm="$1" rm -rf "$mm" mkdir -p "$mm/data/images/portraits/Male/MekWarrior" \ "$mm/data/images/portraits/Female/MekWarrior" \ "$mm/data/images/portraits/Male/Astech" \ "$mm/data/images/portraits/Female/Astech" \ "$mm/data/images/portraits/Black Widow Company" \ "$mm/data/images/hexes/Ahne" \ "$mm/data/images/hexes/largeTextures" \ "$mm/data/images/misc" \ "$mm/data/images/widgets/kiro" \ "$mm/data/boards/unofficial/UlyssesMaps/MegaMek Invitational Map Pack" \ "$mm/data/universe/planetary_systems" \ "$mm/lib" touch "$mm/data/images/portraits/default.gif" \ "$mm/data/images/portraits/Male/MekWarrior/MW_M_999.png" \ "$mm/data/images/portraits/Female/MekWarrior/MW_F_999.png" \ "$mm/data/images/portraits/Male/Astech/AST_M_1.png" \ "$mm/data/images/portraits/Female/Astech/AST_F_1.png" \ "$mm/data/images/portraits/Black Widow Company/Francine.jpg" \ "$mm/data/images/hexes/saxarba.tileset" \ "$mm/data/images/hexes/defaulthexset.txt" \ "$mm/data/images/hexes/largeTextures.tileset" \ "$mm/data/images/hexes/bw_atmospheric.tileset" \ "$mm/data/images/hexes/Ahne/hex.png" \ "$mm/data/images/hexes/largeTextures/hex.png" \ "$mm/data/images/misc/MekHQ.png" \ "$mm/data/images/misc/MekHQ Load_uhd.png" \ "$mm/data/images/misc/MHQcamo1.png" \ "$mm/data/images/misc/mml-background.png" \ "$mm/data/images/misc/mml-splash.jpg" \ "$mm/data/images/misc/mml_logo.jpg" \ "$mm/data/images/misc/megamek_splash_hd.png" \ "$mm/data/images/misc/camo_overlay0.png" \ "$mm/data/images/misc/camo_overlay5.png" \ "$mm/data/images/widgets/kiro/01.gif" \ "$mm/data/images/widgets/kiro/border.psd" \ "$mm/MegaMek.jar" \ "$mm/lib/MegaMek.jar" \ "$mm/lib/commons-io-2.21.0.jar" \ "$mm/data/universe/eras.xml" \ "$mm/data/universe/planetary_systems/canon_systems.zip" # Directories with spaces in them, which the .docx prune has to glob through. touch "$mm/data/boards/unofficial/UlyssesMaps/MegaMek Invitational Map Pack/MegaMek Invitational - Map Pack Rules.docx" \ "$mm/data/boards/unofficial/UlyssesMaps/MegaMek Invitational Map Pack/20x20 Marik Arena - Factory.board" # The portraits the shipped scenarios name, read out of the script rather # than repeated here: whether that list is the right one is settled against # the real tree at build time, by keep. What the fixture is for is that # everything else around them goes. local portrait for portrait in $(portraits_kept); do touch "$(portrait_path "$mm" "$portrait")" done } portraits_kept() { grep -oE 'MekWarrior/MW_[MF]_[0-9]+\.png' "$ROOT/megamek/prune-data.sh" \ | sed 's#.*/##' | sort -u } portrait_path() { # case "$2" in MW_M_*) echo "$1/data/images/portraits/Male/MekWarrior/$2" ;; *) echo "$1/data/images/portraits/Female/MekWarrior/$2" ;; esac } run_prune() { # MM_HOME="$1" bash "$ROOT/megamek/prune-data.sh" 2>&1 } # --- the happy path --------------------------------------------------------- MM="$TMP/mm" stage_tree "$MM" out="$(run_prune "$MM")"; status=$? check "runs clean against a complete tree" test "$status" -eq 0 [ "$status" -eq 0 ] || printf '%s\n' "$out" | sed 's/^/ /' check "unreferenced portraits removed" \ test ! -e "$MM/data/images/portraits/Male/MekWarrior/MW_M_999.png" \ -a ! -e "$MM/data/images/portraits/Female/MekWarrior/MW_F_999.png" # A whole portrait category nothing names. The files go, and so does the # directory they leave behind - an empty tree in the image is still inodes. check "an emptied portrait category removed" \ test ! -e "$MM/data/images/portraits/Male/Astech" \ -a ! -e "$MM/data/images/portraits/Female/Astech" check "unreachable hex sets removed" test ! -e "$MM/data/images/hexes/Ahne" check "orphaned tilesets removed" \ test ! -e "$MM/data/images/hexes/largeTextures.tileset" check "foreign-project art removed" test ! -e "$MM/data/images/misc/MekHQ.png" check "art with a space in the name removed" \ test ! -e "$MM/data/images/misc/MekHQ Load_uhd.png" check "Photoshop source art removed" \ test ! -e "$MM/data/images/widgets/kiro/border.psd" # The pattern globs through two directories whose names contain spaces. A prune # line that names such a path literally would be word-split and match nothing. check "a document under directories with spaces removed" \ test ! -e "$MM/data/boards/unofficial/UlyssesMaps/MegaMek Invitational Map Pack/MegaMek Invitational - Map Pack Rules.docx" # The first prune outside data/, and the one glob that could reach a file the # build stages after this one still compile against. check "the duplicate stock jar removed" test ! -e "$MM/lib/MegaMek.jar" check "MekHQ's planetary systems removed" \ test ! -e "$MM/data/universe/planetary_systems" check "the portrait fallback survives" test -e "$MM/data/images/portraits/default.gif" # An MMSVersion 2 scenario whose messages: block names one of these does not # load without it - MessageDeserializer throws rather than falling back. missing=0 for portrait in $(portraits_kept); do [ -e "$(portrait_path "$MM" "$portrait")" ] || missing=$((missing+1)) done check "every portrait a shipped scenario names survives" test "$missing" -eq 0 check "portrait categories the script never names survive" \ test -e "$MM/data/images/portraits/Black Widow Company/Francine.jpg" check "the kiro art the stock skins name survives" \ test -e "$MM/data/images/widgets/kiro/01.gif" check "boards beside the pruned document survive" \ test -e "$MM/data/boards/unofficial/UlyssesMaps/MegaMek Invitational Map Pack/20x20 Marik Arena - Factory.board" check "the stock jar the build compiles against survives" test -e "$MM/MegaMek.jar" check "the other jars in lib/ survive" test -e "$MM/lib/commons-io-2.21.0.jar" check "the universe data MegaMek does read survives" test -e "$MM/data/universe/eras.xml" check "the default hex set survives" test -e "$MM/data/images/hexes/saxarba.tileset" check "the fallback hex set survives" test -e "$MM/data/images/hexes/defaulthexset.txt" check "MegaMek's own splash art survives" \ test -e "$MM/data/images/misc/megamek_splash_hd.png" # Same directory as the foreign-project art, and no file names them - a glob # there that grew would take a facing's camo shading with it. check "the facing camo overlays survive" \ test -e "$MM/data/images/misc/camo_overlay0.png" -a -e "$MM/data/images/misc/camo_overlay5.png" check "reports what it removed" grep -q "^pruned " <<<"$out" # --- a prune line that no longer matches anything --------------------------- # What an upstream rename looks like. Shipping the bytes silently is the failure # this has to prevent, so the build must stop. MM="$TMP/mm-moved" stage_tree "$MM" rm -rf "$MM/data/images/hexes/Ahne" out="$(run_prune "$MM")"; status=$? check "a prune that matches nothing fails the build" test "$status" -ne 0 check "and names the pattern that missed" grep -q "Ahne" <<<"$out" # --- a kept file that is not there ------------------------------------------ # The prune next to it was written on the assumption that it exists, so its # absence means the assumption needs re-checking, not that the prune is fine. MM="$TMP/mm-nofallback" stage_tree "$MM" rm -f "$MM/data/images/portraits/default.gif" out="$(run_prune "$MM")"; status=$? check "a missing kept file fails the build" test "$status" -ne 0 check "and says which one" grep -q "default.gif" <<<"$out" # --- nothing is pruned before the guards have run --------------------------- # The failure above must leave the tree alone: a build that half-prunes and then # exits non-zero is a cache entry nobody can reason about. check "a failed guard prunes nothing" \ test -e "$MM/data/images/portraits/Male/MekWarrior/MW_M_999.png" # --- a portrait the prune keeps that upstream dropped ----------------------- # The kept list was read off the scenarios; if a name is no longer in the # release the two have drifted and the scan has to be rerun. Carrying on would # put a scenario back on tests/scenarios-known-bad.txt, which is where this # started. MM="$TMP/mm-noportrait" stage_tree "$MM" victim="$(portraits_kept | head -1)" rm -f "$(portrait_path "$MM" "$victim")" out="$(run_prune "$MM")"; status=$? check "a portrait upstream dropped fails the build" test "$status" -ne 0 check "and names the portrait" grep -q "$victim" <<<"$out" echo "$pass passed, $fail failed" [ "$fail" -eq 0 ]