Skip to content

Commit 72806d5

Browse files
committed
v4.0.2
1 parent c0fd843 commit 72806d5

47 files changed

Lines changed: 947 additions & 1439 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/build-cpp.yml

Lines changed: 92 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -502,49 +502,117 @@ jobs:
502502
echo "=== dist/ contents ($(find dist -type f | wc -l) files) ==="
503503
find dist -type f | sort
504504
505-
# ---- Package: macOS (macdeployqt) ----
506-
# Note: upload-artifact strips execute bits (zip limitation).
507-
# We tar the dist/ contents so permissions are preserved on download.
508-
# macdeployqt (copies ~300MB of Qt frameworks) only runs on tag builds;
509-
# push/workflow_dispatch builds skip it to save ~3-5 minutes per job.
505+
# ---- Package: macOS ----
506+
# Order is critical (see issue #139): Python.framework + resources must
507+
# be placed INSIDE the .app BEFORE macdeployqt / codesign run. If we
508+
# copy them as siblings of the .app (old behavior) the bundle can't find
509+
# them at runtime; if we embed after signing, dyld on macOS 14+ kills
510+
# the process with SIGKILL (Code Signature Invalid).
511+
#
512+
# On tag / workflow_dispatch: full stage -> macdeployqt -> deep ad-hoc
513+
# codesign -> verify. On push builds: stage + skip macdeployqt/codesign
514+
# (saves ~3-5 min; the resulting bundle won't run on stock macOS but is
515+
# fine as a CI artifact for inspection).
516+
#
517+
# upload-artifact strips execute bits (zip limitation) so we tar dist/.
510518
- name: Package (macOS)
511519
if: runner.os == 'macOS'
512520
working-directory: fincept-qt
513521
run: |
522+
set -euo pipefail
514523
mkdir -p dist
515524
MACDEPLOYQT="${QT_ROOT_DIR}/bin/macdeployqt"
516-
APP="build/${{ matrix.exe }}.app"
517525
IS_TAG="${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}"
518-
if [ -d "$APP" ]; then
519-
# .app bundle exists — run macdeployqt only on tag builds
520-
if [ "$IS_TAG" = "true" ]; then
521-
"$MACDEPLOYQT" "$APP" 2>/dev/null || true
522-
fi
523-
cp -r "$APP" dist/
526+
527+
# ── Locate or synthesize .app bundle ──────────────────────────────
528+
if [ -d "build/${{ matrix.exe }}.app" ]; then
529+
APP_BUNDLE="build/${{ matrix.exe }}.app"
524530
else
525-
# Plain binary — create a minimal .app bundle manually
526-
BUNDLE="dist/${{ matrix.exe }}.app"
527-
mkdir -p "$BUNDLE/Contents/MacOS"
528-
cp "build/${{ matrix.exe }}" "$BUNDLE/Contents/MacOS/${{ matrix.exe }}"
529-
chmod +x "$BUNDLE/Contents/MacOS/${{ matrix.exe }}"
530-
cat > "$BUNDLE/Contents/Info.plist" <<'PLIST'
531+
APP_BUNDLE="build/${{ matrix.exe }}.app"
532+
mkdir -p "${APP_BUNDLE}/Contents/MacOS"
533+
cp "build/${{ matrix.exe }}" "${APP_BUNDLE}/Contents/MacOS/${{ matrix.exe }}"
534+
chmod +x "${APP_BUNDLE}/Contents/MacOS/${{ matrix.exe }}"
535+
cat > "${APP_BUNDLE}/Contents/Info.plist" <<'PLIST'
531536
<?xml version="1.0" encoding="UTF-8"?>
532537
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
533538
<plist version="1.0"><dict>
534539
<key>CFBundleExecutable</key><string>FinceptTerminal</string>
535-
<key>CFBundleIdentifier</key><string>in.fincept.terminal</string>
540+
<key>CFBundleIdentifier</key><string>com.fincept.terminal</string>
536541
<key>CFBundleName</key><string>FinceptTerminal</string>
537542
<key>CFBundlePackageType</key><string>APPL</string>
538-
<key>CFBundleShortVersionString</key><string>4.0.1</string>
543+
<key>CFBundleShortVersionString</key><string>4.0.2</string>
544+
<key>CFBundleVersion</key><string>4.0.2</string>
545+
<key>LSMinimumSystemVersion</key><string>13.0</string>
539546
<key>NSHighResolutionCapable</key><true/>
540547
</dict></plist>
541548
PLIST
542-
if [ "$IS_TAG" = "true" ]; then
543-
"$MACDEPLOYQT" "$BUNDLE" 2>/dev/null || true
549+
fi
550+
551+
# ── Stage resources INSIDE the bundle (not as siblings) ───────────
552+
mkdir -p "${APP_BUNDLE}/Contents/Resources"
553+
cp -r resources "${APP_BUNDLE}/Contents/Resources/" 2>/dev/null || true
554+
cp -r scripts "${APP_BUNDLE}/Contents/Resources/scripts" 2>/dev/null || true
555+
556+
# ── Embed Python.framework (tag builds only — skip on push) ───────
557+
if [ "$IS_TAG" = "true" ]; then
558+
PY_FRAMEWORK=""
559+
for candidate in \
560+
"/Library/Frameworks/Python.framework" \
561+
"$(brew --prefix python@3.12 2>/dev/null)/Frameworks/Python.framework" \
562+
"$(brew --prefix python3 2>/dev/null)/Frameworks/Python.framework"; do
563+
[ -d "${candidate}/Versions" ] && { PY_FRAMEWORK="${candidate}"; break; }
564+
done
565+
BUNDLE_FRAMEWORKS="${APP_BUNDLE}/Contents/Frameworks"
566+
mkdir -p "${BUNDLE_FRAMEWORKS}"
567+
if [ -n "${PY_FRAMEWORK}" ]; then
568+
echo "Embedding Python.framework from: ${PY_FRAMEWORK}"
569+
rsync -a --copy-unsafe-links "${PY_FRAMEWORK}/" "${BUNDLE_FRAMEWORKS}/Python.framework/" 2>/dev/null || \
570+
cp -R "${PY_FRAMEWORK}" "${BUNDLE_FRAMEWORKS}/" 2>/dev/null || true
571+
find "${BUNDLE_FRAMEWORKS}/Python.framework" -name "PrivateHeaders" -type l \
572+
! -exec test -e {} \; -delete 2>/dev/null || true
573+
PY_VER="$(python3 -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")')"
574+
ln -sf "../Frameworks/Python.framework/Versions/${PY_VER}/bin/python${PY_VER}" \
575+
"${APP_BUNDLE}/Contents/MacOS/python3" 2>/dev/null || true
576+
else
577+
echo "::warning::Python.framework not found — bundle will rely on system python"
544578
fi
545579
fi
546-
cp -r resources dist/resources 2>/dev/null || true
547-
cp -r scripts dist/scripts 2>/dev/null || true
580+
581+
# ── Run macdeployqt on the fully staged bundle (tag builds only) ──
582+
if [ "$IS_TAG" = "true" ] && [ -x "$MACDEPLOYQT" ]; then
583+
echo "Running macdeployqt on ${APP_BUNDLE}"
584+
"$MACDEPLOYQT" "${APP_BUNDLE}" -verbose=1 || \
585+
echo "::warning::macdeployqt exited non-zero — continuing to codesign"
586+
fi
587+
588+
# ── Deep ad-hoc codesign (tag builds only) — fix for issue #139 ───
589+
# Strip every signature leaf-to-root, then re-sign whole bundle with
590+
# the ad-hoc identity ("-"). Do NOT pass --preserve-metadata: there
591+
# are no prior entitlements to preserve on a fresh bundle and the
592+
# flag errors out on resource-fork detritus left by macdeployqt.
593+
if [ "$IS_TAG" = "true" ]; then
594+
echo "Stripping existing signatures..."
595+
find "${APP_BUNDLE}" -type f \( -name "*.dylib" -o -name "*.so" -o -perm +111 \) 2>/dev/null | \
596+
while read -r f; do
597+
if file "$f" 2>/dev/null | grep -q "Mach-O"; then
598+
codesign --remove-signature "$f" 2>/dev/null || true
599+
fi
600+
done
601+
codesign --remove-signature "${APP_BUNDLE}" 2>/dev/null || true
602+
603+
echo "Re-signing bundle ad-hoc (deep)..."
604+
codesign --force --deep --sign - --timestamp=none "${APP_BUNDLE}"
605+
606+
echo "Verifying signature..."
607+
codesign --verify --deep --strict --verbose=2 "${APP_BUNDLE}" || {
608+
echo "::error::Bundle verification failed"
609+
codesign --display --deep --verbose=4 "${APP_BUNDLE}" || true
610+
exit 1
611+
}
612+
fi
613+
614+
# ── Copy signed bundle to dist/ ──────────────────────────────────
615+
cp -R "${APP_BUNDLE}" dist/
548616
# Tar to preserve execute permissions (upload-artifact uses zip which strips them)
549617
tar -czf dist.tar.gz -C dist .
550618

0 commit comments

Comments
 (0)