Skip to content

Extend macOS bundle script with release checks#779

Open
iefiru wants to merge 1 commit into
solvcon:masterfrom
iefiru:macos-release-workflow
Open

Extend macOS bundle script with release checks#779
iefiru wants to merge 1 commit into
solvcon:masterfrom
iefiru:macos-release-workflow

Conversation

@iefiru
Copy link
Copy Markdown

@iefiru iefiru commented May 14, 2026

Add release-oriented subcommands to bundle-with-homebrew.sh: check, bundle, verify, and all. The new check step validates the Homebrew-based macOS bundling environment before packaging, while verify can inspect a generated DMG artifact.

Add Makefile entry points for the release workflow. These targets delegate to the bundle script instead of implementing release logic in Makefile:

  • make release-check
  • make release
  • make release-test

make release now runs the check step before bundling.

Related to issue #765

Add release-oriented subcommands to bundle-with-homebrew.sh: check, bundle, verify, and all. The new check step validates the Homebrew-based macOS bundling environment before packaging, while verify can inspect a generated DMG artifact.

Add Makefile entry points for the release workflow. These targets delegate to the bundle script instead of implementing release logic in Makefile:

- make release-check
- make release
- make release-test

make release now runs the check step before bundling.
@yungyuc yungyuc added the build Build system and automation label May 14, 2026
@yungyuc
Copy link
Copy Markdown
Member

yungyuc commented May 14, 2026

Please use the following format to refer to the associated issue in the future:

Related to issue #765

You used "Fixes number" in the PR comment and I changed it. See Pull Request Guidelines:

When opening a pull request, reference the related issue (e.g., "Related to #725") instead of using closing keywords like "close numer", "closes number", or "fixes number". We do not let PR and commit log comments to mandate the management.

Copy link
Copy Markdown
Author

@iefiru iefiru left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added inline comments around the release workflow. Feel free to share any questions or suggestions.

Comment thread Makefile
Comment on lines +169 to +182
.PHONY: release-check
release-check:
$(MODMESH_ROOT)/contrib/bundle/bundle-with-homebrew.sh check

.PHONY: release
release:
$(MODMESH_ROOT)/contrib/bundle/bundle-with-homebrew.sh all \
--output "$(RELEASE_OUTPUT)" $(RELEASE_ARGS)

.PHONY: release-test
release-test:
$(MODMESH_ROOT)/contrib/bundle/bundle-with-homebrew.sh verify \
"$(RELEASE_ARTIFACT)"

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • release-check checks the bundle dependencies.
  • release checks the dependencies and builds the DMG bundle.
  • release-test tests the DMG bundle.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discuss: I propose to use explicit names for macos: bundle-precheck (from release-check), bundle (from release), bundle-test (release-test).

Comment on lines +405 to +409
verify)
[[ $# -eq 1 ]] || { usage >&2; exit 2; }
VERIFY_ARTIFACT="$1"
shift
;;
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MUST provide DMG file for verify.

cd "$BUNDLE_REPO_ROOT"

MIN_LOADS=${MIN_LOADS:-50}
HOST_PREFIX_RE='^(/opt/homebrew|/usr/local)'
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Centralize host package path patterns for easier management and reuse.

Comment on lines +49 to +53
Subcommands:
check Check macOS bundle release dependencies. Does not build or install.
bundle Build/package pilot.app and pilot.dmg with Homebrew dependencies.
verify Verify a generated release DMG artifact.
all Run check, then bundle.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • check checks the bundle dependencies.
  • bundle checks the dependencies and builds the DMG bundle.
  • verify tests the DMG bundle.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If all does not include verify, make it explicit like:

Run check and then bundle. (No verify.)

Comment on lines +95 to +118
python_module_check() {
local module="$1" hint="${2:-}" out
out=$(mktemp -t modmesh-bundle-pycheck)
if python3 - "$module" <<'PY' >"$out" 2>&1
import importlib
import sys
module = sys.argv[1]
try:
mod = importlib.import_module(module)
except Exception as e:
print(f"{type(e).__name__}: {e}")
raise SystemExit(1)
else:
print(getattr(mod, "__file__", "built-in"))
PY
then
ok "python module $module: $(cat "$out")"
rm -f "$out"
else
cat "$out" >&2 || true
rm -f "$out"
fail "python module $module: missing${hint:+ ($hint)}"
fi
}
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import the given Python package to verify it is available.

Comment on lines +137 to +152
check_pybind11_cmake() {
local config_dir prefix
if command -v pybind11-config >/dev/null 2>&1; then
config_dir=$(pybind11-config --cmakedir 2>/dev/null || true)
if [[ -n "$config_dir" && -f "$config_dir/pybind11Config.cmake" ]]; then
ok "pybind11 CMake config: $config_dir/pybind11Config.cmake"
return 0
fi
fi
prefix=$(brew --prefix pybind11 2>/dev/null || true)
if [[ -n "$prefix" && -f "$prefix/share/cmake/pybind11/pybind11Config.cmake" ]]; then
ok "pybind11 CMake config: $prefix/share/cmake/pybind11/pybind11Config.cmake"
else
fail "pybind11 CMake config: not found (brew install pybind11)"
fi
}
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure pybind11's CMake config is available before the build step.

Comment on lines +154 to +198
check_deps() {
local brew_exe brew_prefix python_path fw_path failed=0
require_macos

if brew_exe=$(find_brew); then
ok "brew: $brew_exe"
eval "$("$brew_exe" shellenv)"
brew_prefix=$(brew --prefix)
ok "Homebrew prefix: $brew_prefix"
else
fail "brew: not found (install Homebrew)" || true
setup_hint
exit 1
fi

for cmd in cmake make python3 macdeployqt qtpaths otool \
install_name_tool codesign hdiutil rsync shasum; do
require_command "$cmd" || failed=1
done

python_path=$(command -v python3 || true)
if [[ -n "$python_path" && "$python_path" == "$brew_prefix"/* ]]; then
ok "python3 is under Homebrew prefix"
else
fail "python3 is not from Homebrew prefix ($python_path; expected under $brew_prefix)" || failed=1
fi

if fw_path=$(python_framework_check 2>&1); then
ok "Python framework dylib: $fw_path"
else
fail "$fw_path" || failed=1
fi

check_pybind11_cmake || failed=1
python_module_check numpy "brew install numpy" || failed=1
python_module_check PySide6 "brew install pyside" || failed=1
python_module_check shiboken6 "brew install pyside" || failed=1
python_module_check shiboken6_generator "brew install pyside" || failed=1
python_module_check matplotlib "brew install python-matplotlib" || failed=1

if [[ $failed -ne 0 ]]; then
setup_hint
exit 1
fi
}
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement the check subcommand by running the macOS release preflight checks.

Comment on lines +269 to +278
verify_app_structure() {
local app="$1" bin
[[ -d "$app" ]] || fail "app not found: $app"
[[ -f "$app/Contents/Info.plist" ]] || fail "Info.plist not found"
bin="$app/Contents/MacOS/$(basename "$app" .app)"
[[ -x "$bin" ]] || fail "main binary not executable: $bin"
[[ -d "$app/Contents/Frameworks" ]] || fail "Contents/Frameworks not found"
[[ -d "$app/Contents/Resources" ]] || fail "Contents/Resources not found"
file "$bin"
}
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check that the DMG contains a complete macOS app bundle.

Comment on lines +288 to +317
smoke_launch_verify() {
local bin="$1" trace="$2" pid elapsed=0 loaded
: > "$trace"
env -i HOME="${HOME:-/}" USER="${USER:-nobody}" \
PATH=/usr/bin:/bin TERM="${TERM:-xterm}" \
DYLD_PRINT_LIBRARIES=1 \
"$bin" >/dev/null 2>"$trace" &
pid=$!
while [[ $elapsed -lt 15 ]]; do
sleep 1
elapsed=$((elapsed + 1))
loaded=$(grep -c '^dyld\[' "$trace" 2>/dev/null || true)
[[ ${loaded:-0} -ge $MIN_LOADS ]] && break
done
kill "$pid" 2>/dev/null || true
wait "$pid" 2>/dev/null || true

loaded=$(grep -c '^dyld\[' "$trace" 2>/dev/null || true)
if [[ $loaded -lt $MIN_LOADS ]]; then
echo "ERROR: pilot loaded only $loaded libraries; did it crash early?" >&2
sed -n '1,80p' "$trace" >&2
return 1
fi
if grep -E '^dyld\[' "$trace" | grep -E '/opt/homebrew|/usr/local' >/dev/null; then
echo "ERROR: smoke launch loaded libraries from host package prefixes:" >&2
grep -E '^dyld\[' "$trace" | grep -E '/opt/homebrew|/usr/local' | head -20 >&2
return 1
fi
echo " OK: $loaded libraries loaded, none from host package prefixes"
}
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Launch the app from the mounted DMG and verify its dyld trace does not load host package libraries.

Comment on lines +330 to +384
verify_dmg() {
local dmg="$1" mnt tmp trace marker app bin copied_app
require_macos
[[ -f "$dmg" ]] || fail "release artifact not found: $dmg"

note "Release artifact"
ls -lh "$dmg"
shasum -a 256 "$dmg"
hdiutil imageinfo "$dmg" >/dev/null

VERIFY_MNT=$(mktemp -d -t modmesh-release-dmg)
VERIFY_TMP=$(mktemp -d -t modmesh-release-app)
VERIFY_TRACE=$(mktemp -t modmesh-release-dyld)
VERIFY_MARKER=$(mktemp -t modmesh-release-marker)
mnt="$VERIFY_MNT"
tmp="$VERIFY_TMP"
trace="$VERIFY_TRACE"
marker="$VERIFY_MARKER"
trap cleanup_verify EXIT

note "Mounting DMG"
hdiutil attach -nobrowse -readonly -mountpoint "$mnt" "$dmg" >/dev/null
app=$(find "$mnt" -maxdepth 2 -name '*.app' -type d -print -quit)
[[ -n "$app" ]] || fail "no .app found in $dmg"
bin="$app/Contents/MacOS/$(basename "$app" .app)"

note "Checking mounted app structure"
verify_app_structure "$app"

note "Checking code signature before launch"
codesign --verify --deep --strict "$app"
spctl --assess --type execute --verbose=4 "$app" 2>&1 || \
echo " warning: spctl rejected this app (expected for ad-hoc signed prototype builds)"

note "Static scan for host package paths"
check_self_contained "$app"

note "Runtime import check from a writable app copy"
cp -R "$app" "$tmp/"
copied_app="$tmp/$(basename "$app")"
codesign --verify --deep --strict "$copied_app"
touch "$marker"
runtime_import_check "$copied_app/Contents/MacOS/$(basename "$copied_app" .app)"
if find "$copied_app" -name '*.pyc' -newer "$marker" -print -quit | grep -q .; then
echo "ERROR: runtime import wrote Python bytecode into the signed app bundle:" >&2
find "$copied_app" -name '*.pyc' -newer "$marker" -print | sed -n '1,20p' >&2
return 1
fi
codesign --verify --deep --strict "$copied_app"

note "Smoke launch from mounted DMG"
smoke_launch_verify "$bin" "$trace"

note "Release artifact verification passed"
}
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement the verify subcommand by validating the generated DMG as a release artifact.

Copy link
Copy Markdown
Member

@yungyuc yungyuc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • If all does not include verify, make it explicit like.
  • Discuss: I propose to use explicit names for macos: bundle-precheck (from release-check), bundle (from release), bundle-test (release-test).

Comment on lines +49 to +53
Subcommands:
check Check macOS bundle release dependencies. Does not build or install.
bundle Build/package pilot.app and pilot.dmg with Homebrew dependencies.
verify Verify a generated release DMG artifact.
all Run check, then bundle.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If all does not include verify, make it explicit like:

Run check and then bundle. (No verify.)

Comment thread Makefile
Comment on lines +169 to +182
.PHONY: release-check
release-check:
$(MODMESH_ROOT)/contrib/bundle/bundle-with-homebrew.sh check

.PHONY: release
release:
$(MODMESH_ROOT)/contrib/bundle/bundle-with-homebrew.sh all \
--output "$(RELEASE_OUTPUT)" $(RELEASE_ARGS)

.PHONY: release-test
release-test:
$(MODMESH_ROOT)/contrib/bundle/bundle-with-homebrew.sh verify \
"$(RELEASE_ARTIFACT)"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discuss: I propose to use explicit names for macos: bundle-precheck (from release-check), bundle (from release), bundle-test (release-test).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build Build system and automation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants