@@ -285,9 +285,12 @@ jobs:
285285 detach_attempts=3
286286 detach_sleep_seconds=2
287287 rw_dmg_image_prefix='/src-tauri/target/'
288- rw_dmg_image_suffix_regex='/bundle/macos/rw\\..*\\.dmg$'
288+ rw_dmg_image_suffix_regex='/bundle/macos/rw\..*\.dmg$'
289+ allow_global_helper_cleanup="${ASTRBOT_DESKTOP_MACOS_ALLOW_GLOBAL_HELPER_KILL:-0}"
289290 log_file=""
290291 workspace_root="${GITHUB_WORKSPACE:-$(pwd)}"
292+ workspace_root_raw="${workspace_root%/}"
293+ workspace_root="${workspace_root_raw}"
291294 workspace_root="${workspace_root%/}"
292295
293296 cleanup_log_file() {
@@ -313,23 +316,32 @@ jobs:
313316 return 1
314317 }
315318
316- cleanup_stale_dmg_state() {
317- local dmg_mounts
318- dmg_mounts="$(mount | awk '$3 ~ /^\/Volumes\/dmg\./ { print $3 }' || true)"
319- if [ -n "${dmg_mounts}" ]; then
320- while IFS= read -r mount_point; do
321- [ -z "${mount_point}" ] && continue
322- echo "Detaching stale mount ${mount_point}"
323- detach_target "${mount_point}" || true
324- done <<< "${dmg_mounts}"
325- fi
319+ canonicalize_path() {
320+ local raw_path="$1"
321+ python3 -c 'import os,sys; print(os.path.realpath(sys.argv[1]))' "${raw_path}"
322+ }
326323
327- local workspace_disks
328- workspace_disks="$(hdiutil info 2>/dev/null | awk \
329- -v root="${workspace_root}" \
330- -v image_prefix="${rw_dmg_image_prefix}" \
331- -v image_suffix_re="${rw_dmg_image_suffix_regex}" '
332- BEGIN { image = ""; dev = "" }
324+ is_workspace_rw_dmg_image() {
325+ local image="$1"
326+ local normalized_image
327+ normalized_image="$(canonicalize_path "${image}")"
328+ local candidate
329+ for candidate in "${image}" "${normalized_image}"; do
330+ if [[ "${candidate}" == "${workspace_root}${rw_dmg_image_prefix}"* ]] &&
331+ [[ "${candidate}" =~ ${rw_dmg_image_suffix_regex} ]]; then
332+ return 0
333+ fi
334+ if [[ "${candidate}" == "${workspace_root_raw}${rw_dmg_image_prefix}"* ]] &&
335+ [[ "${candidate}" =~ ${rw_dmg_image_suffix_regex} ]]; then
336+ return 0
337+ fi
338+ done
339+ return 1
340+ }
341+
342+ collect_dmg_records() {
343+ hdiutil info 2>/dev/null | awk '
344+ BEGIN { image = ""; dev = ""; pid = "" }
333345 /^image-path[[:space:]]*:/ {
334346 line = $0
335347 sub(/^image-path[[:space:]]*:[[:space:]]*/, "", line)
@@ -340,24 +352,70 @@ jobs:
340352 dev = $1
341353 next
342354 }
355+ /^process ID[[:space:]]*:/ {
356+ line = $0
357+ sub(/^process ID[[:space:]]*:[[:space:]]*/, "", line)
358+ pid = line
359+ next
360+ }
343361 /^=+/ {
344- if (index(image, root image_prefix) == 1 &&
345- image ~ image_suffix_re &&
346- dev != "") {
347- print dev
362+ if (image != "") {
363+ print image "\t" dev "\t" pid
348364 }
349365 image = ""
350366 dev = ""
367+ pid = ""
351368 next
352369 }
353370 END {
354- if (index(image, root image_prefix) == 1 &&
355- image ~ image_suffix_re &&
356- dev != "") {
357- print dev
371+ if (image != "") {
372+ print image "\t" dev "\t" pid
358373 }
359374 }
360- ' | sort -u)"
375+ '
376+ }
377+
378+ terminate_pid_soft_then_hard() {
379+ local pid="$1"
380+ kill -TERM "${pid}" 2>/dev/null || return 0
381+ sleep 1
382+ if kill -0 "${pid}" 2>/dev/null; then
383+ kill -KILL "${pid}" 2>/dev/null || true
384+ fi
385+ }
386+
387+ cleanup_stale_dmg_state() {
388+ local dmg_mounts
389+ dmg_mounts="$(mount | awk '$3 ~ /^\/Volumes\/dmg\./ { print $3 }' || true)"
390+ if [ -n "${dmg_mounts}" ]; then
391+ while IFS= read -r mount_point; do
392+ [ -z "${mount_point}" ] && continue
393+ echo "Detaching stale mount ${mount_point}"
394+ detach_target "${mount_point}" || true
395+ done <<< "${dmg_mounts}"
396+ fi
397+
398+ local dmg_records
399+ dmg_records="$(collect_dmg_records)"
400+
401+ local workspace_disks
402+ workspace_disks=""
403+ local workspace_helper_pids
404+ workspace_helper_pids=""
405+ while IFS=$'\t' read -r image disk pid; do
406+ [ -z "${image:-}" ] && continue
407+ if ! is_workspace_rw_dmg_image "${image}"; then
408+ continue
409+ fi
410+ if [[ "${disk}" =~ ^/dev/disk[0-9]+$ ]]; then
411+ workspace_disks+="${disk}"$'\n'
412+ fi
413+ if [[ "${pid}" =~ ^[0-9]+$ ]]; then
414+ workspace_helper_pids+="${pid}"$'\n'
415+ fi
416+ done <<< "${dmg_records}"
417+ workspace_disks="$(printf '%s\n' "${workspace_disks}" | awk 'NF' | sort -u)"
418+ workspace_helper_pids="$(printf '%s\n' "${workspace_helper_pids}" | awk 'NF' | sort -u)"
361419
362420 if [ -n "${workspace_disks}" ]; then
363421 while IFS= read -r disk; do
@@ -369,55 +427,23 @@ jobs:
369427
370428 # Prefer process IDs linked to this workspace's RW DMG images.
371429 local helper_pids
372- helper_pids="$(hdiutil info 2>/dev/null | awk \
373- -v root="${workspace_root}" \
374- -v image_prefix="${rw_dmg_image_prefix}" \
375- -v image_suffix_re="${rw_dmg_image_suffix_regex}" '
376- BEGIN { image = ""; pid = "" }
377- /^image-path[[:space:]]*:/ {
378- line = $0
379- sub(/^image-path[[:space:]]*:[[:space:]]*/, "", line)
380- image = line
381- next
382- }
383- /^process ID[[:space:]]*:/ {
384- line = $0
385- sub(/^process ID[[:space:]]*:[[:space:]]*/, "", line)
386- pid = line
387- next
388- }
389- /^=+/ {
390- if (index(image, root image_prefix) == 1 &&
391- image ~ image_suffix_re &&
392- pid ~ /^[0-9]+$/) {
393- print pid
394- }
395- image = ""
396- pid = ""
397- next
398- }
399- END {
400- if (index(image, root image_prefix) == 1 &&
401- image ~ image_suffix_re &&
402- pid ~ /^[0-9]+$/) {
403- print pid
404- }
405- }
406- ' | sort -u)"
430+ helper_pids="${workspace_helper_pids}"
407431
408432 # Fallback: exact process-name match only, avoid broad -f substring patterns.
409- if [ -z "${helper_pids}" ]; then
433+ if [ -z "${helper_pids}" ] && [ "${allow_global_helper_cleanup}" = "1" ] ; then
410434 helper_pids="$(
411435 pgrep -x diskimages-helper || true
412436 pgrep -x diskimages-help || true
413437 )"
438+ elif [ -z "${helper_pids}" ]; then
439+ echo "Skip global disk image helper cleanup (set ASTRBOT_DESKTOP_MACOS_ALLOW_GLOBAL_HELPER_KILL=1 to enable)." >&2
414440 fi
415441 helper_pids="$(printf '%s\n' "${helper_pids}" | awk 'NF' | sort -u)"
416442 if [ -n "${helper_pids}" ]; then
417443 while IFS= read -r pid; do
418444 [ -z "${pid}" ] && continue
419445 echo "Killing stale disk image helper pid=${pid}"
420- kill "${pid}" || true
446+ terminate_pid_soft_then_hard "${pid}"
421447 done <<< "${helper_pids}"
422448 fi
423449 }
0 commit comments