From ab137cbfe2763f2306e52c7c5f1860d87defc636 Mon Sep 17 00:00:00 2001 From: fanquake Date: Tue, 10 Mar 2026 16:42:59 +0000 Subject: [PATCH 1/3] macdeploy: subprocess out to zip rather than shutil.make_archive Calling shutil.make_archive(), does not preserve symlinks when using the zip format, see https://github.com/python/cpython/issues/139679. Call `zip` using subprocess instead. This code is only run when using a macos machine, and I think it's safe to assume that zip is available, same as codesign, and all other tools we call in this script. --- contrib/macdeploy/macdeployqtplus | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus index 476ac133ebf..dc03d710889 100755 --- a/contrib/macdeploy/macdeployqtplus +++ b/contrib/macdeploy/macdeployqtplus @@ -499,7 +499,7 @@ if config.zip is not None: print("+ Removing existing .zip +") os.unlink(name + ".zip") - shutil.make_archive('{}'.format(name), format='zip', root_dir='dist', base_dir='Bitcoin-Qt.app') + subprocess.check_call(["zip", "-ry", os.path.abspath(name + ".zip"), 'Bitcoin-Qt.app'], cwd='dist') # ------------------------------------------------ From 66d80d57b48982d8301e21321a9d82780586908d Mon Sep 17 00:00:00 2001 From: fanquake Date: Wed, 11 Mar 2026 10:33:01 +0000 Subject: [PATCH 2/3] macdeploy: use plugins dir to find plugins Rather than looking for /translations, which might not exist (it doesn't in a recent brew installed qt on macOS). i.e: ```bash ls /opt/homebrew/opt/qtbase/share/qt doc libexec metatypes mkspecs modules plugins sbom ``` --- contrib/macdeploy/macdeployqtplus | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/macdeploy/macdeployqtplus b/contrib/macdeploy/macdeployqtplus index dc03d710889..bb693e73058 100755 --- a/contrib/macdeploy/macdeployqtplus +++ b/contrib/macdeploy/macdeployqtplus @@ -160,7 +160,7 @@ class DeploymentInfo(object): def detectQtPath(self, frameworkDirectory: str): parentDir = os.path.dirname(frameworkDirectory) - if os.path.exists(os.path.join(parentDir, "share", "qt", "translations")): + if os.path.exists(os.path.join(parentDir, "share", "qt", "plugins")): self.qtPath = parentDir else: self.qtPath = os.getenv("QTDIR", None) From d03e3be246f64065002268e74ee9a834089de37a Mon Sep 17 00:00:00 2001 From: fanquake Date: Tue, 10 Mar 2026 12:08:05 +0000 Subject: [PATCH 3/3] ci: check macos bundle structure and codesigning --- ci/test/03_test_script.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ci/test/03_test_script.sh b/ci/test/03_test_script.sh index eb03c2dd493..8af2ceecf38 100755 --- a/ci/test/03_test_script.sh +++ b/ci/test/03_test_script.sh @@ -137,6 +137,14 @@ if [ "$RUN_CHECK_DEPS" = "true" ]; then "${BASE_ROOT_DIR}/contrib/devtools/check-deps.sh" "${BASE_BUILD_DIR}" fi +if [[ "$CI_OS_NAME" == "macos" && "${GOAL}" = "install deploy" ]]; then + unzip "${BASE_BUILD_DIR}/bitcoin-macos-app.zip" -d "${BASE_BUILD_DIR}/deploy" + if ! ( codesign --verify "${BASE_BUILD_DIR}/deploy/Bitcoin-Qt.app" ); then + echo "Codesigning failed." + false + fi +fi + if [ "$RUN_UNIT_TESTS" = "true" ]; then DIR_UNIT_TEST_DATA="${DIR_UNIT_TEST_DATA}" \ LD_LIBRARY_PATH="${DEPENDS_DIR}/${HOST}/lib" \