Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions corpus/skills/cat-mode/scripts/update_fleet.sh
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,6 @@ app_version() {

parked_app() {
local candidate
[ -d "$APP_DIR/Invoker.app" ] && return 0
for candidate in "$APP_DIR"/Invoker.app.replacing.*; do
[ -d "$candidate" ] || continue
printf '%s' "$candidate"
Expand All @@ -192,7 +191,7 @@ local_app() {
parked="$(parked_app)"
if [ "$DRY_RUN" = 1 ]; then
if [ -n "$parked" ]; then
row warn local "app: an interrupted run left $parked and no $APP_DIR/Invoker.app; a real run puts it back first (dry-run)" "$parked"
row warn local "app: an interrupted run left $parked; a real run puts it back over any $APP_DIR/Invoker.app first (dry-run)" "$parked"
return 0
fi
row ok local "app $(app_version) -> $RELEASE_VERSION (dry-run)" ""; return 0
Expand Down
34 changes: 32 additions & 2 deletions tests/test_cat_mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ def test_script_carries_no_comments(self):
APP_DIR="$TEST_APP_DIR"
WORK_DIR="$TEST_WORK_DIR"
RELEASE_VERSION="9.9.9"
DRY_RUN=0
DRY_RUN="${{TEST_DRY_RUN:-0}}"
row() {{ printf '%s\\t%s\\t%s\\n' "$1" "$2" "$3" >> "$TEST_ROWS"; return 0; }}
fetch_asset() {{ printf '%s' "$WORK_DIR/Invoker.dmg"; }}
{functions}
Expand Down Expand Up @@ -754,7 +754,7 @@ def test_script_carries_no_comments(self):
FAILING_CP = "exit 1\n"


def run_local_app(script_path, tmp, dmg_version="9.9.9", cp_fails=False):
def run_local_app(script_path, tmp, dmg_version="9.9.9", cp_fails=False, dry_run=False):
"""Run update_fleet.sh's app-replace step alone, against a fake /Applications.

The local-Mac section is sliced out of the real script and sourced into a
Expand Down Expand Up @@ -800,6 +800,7 @@ def run_local_app(script_path, tmp, dmg_version="9.9.9", cp_fails=False):
TEST_WORK_DIR=work_dir,
TEST_ROWS=rows,
TEST_DMG_VERSION=dmg_version,
TEST_DRY_RUN="1" if dry_run else "0",
)
out = subprocess.run(["bash", harness], capture_output=True, text=True, env=env)
with open(rows, encoding="utf-8") as handle:
Expand Down Expand Up @@ -887,6 +888,35 @@ def test_a_good_replace_reports_ok_and_keeps_the_previous_bundle(self):
leftovers = [n for n in os.listdir(app_dir) if ".replacing." in n]
self.assertEqual(leftovers, [], f"parked bundle left behind: {leftovers}")

def test_a_park_beside_a_killed_copy_is_put_back_before_the_retry(self):
"""A SIGKILL mid `cp -R` cannot be trapped. It leaves a partial
Invoker.app beside the parked original. The next run has to trust the
park, not the partial, or a failed retry restores the partial and the
good bundle is lost."""
app_dir = os.path.join(self.tmp, "Applications")
os.makedirs(app_dir, exist_ok=True)
write_bundle(app_dir, "Invoker.app", "partial")
write_bundle(app_dir, "Invoker.app.replacing.4242", "1.0.0")

app_dir, row, out = run_local_app(self.SCRIPT, self.tmp, cp_fails=True)

self.assertTrue(row.startswith("fail\t"), f"row was {row!r}\n{out.stderr}")
self.assertEqual(bundle_version(app_dir, "Invoker.app"), "1.0.0", row)
leftovers = [n for n in os.listdir(app_dir) if ".replacing." in n]
self.assertEqual(leftovers, [], f"parked bundle left behind: {leftovers}")

def test_dry_run_warns_on_a_park_beside_a_killed_copy(self):
app_dir = os.path.join(self.tmp, "Applications")
os.makedirs(app_dir, exist_ok=True)
write_bundle(app_dir, "Invoker.app", "partial")
write_bundle(app_dir, "Invoker.app.replacing.4242", "1.0.0")

app_dir, row, out = run_local_app(self.SCRIPT, self.tmp, dry_run=True)

self.assertTrue(row.startswith("warn\t"), f"row was {row!r}\n{out.stderr}")
self.assertIn("Invoker.app.replacing.4242", row)
self.assertEqual(bundle_version(app_dir, "Invoker.app.replacing.4242"), "1.0.0", row)


CATSTACK_FUNCTIONS = re.compile(r"^write_payloads\(\) \{.*?(?=^write_payloads$)", re.S | re.M)

Expand Down
Loading