From 30febbe3d2e9dd38585ab11a8a38109463da4d9f Mon Sep 17 00:00:00 2001 From: "John W. Parent" <45471568+johnwparent@users.noreply.github.com> Date: Tue, 25 Jun 2024 20:31:51 -0400 Subject: [PATCH] Add basic Macos signing + notarizing workflow (#2319) Adds basic CircleCI workflow to sign, notarize, and staple MacOS app bundle and associated DMG, then publishes signed binary in CircleCI artifacts Signed-off-by: Adam Treat --- .circleci/continue_config.yml | 88 +++++++++++++++++++++++++++++++++++ .circleci/grab_notary_id.py | 17 +++++++ 2 files changed, 105 insertions(+) create mode 100644 .circleci/grab_notary_id.py diff --git a/.circleci/continue_config.yml b/.circleci/continue_config.yml index e725cdf5..1c50ddbe 100644 --- a/.circleci/continue_config.yml +++ b/.circleci/continue_config.yml @@ -77,8 +77,90 @@ jobs: ~/Qt/Tools/CMake/CMake.app/Contents/bin/cmake --build . --target package mkdir upload cp gpt4all-installer-* upload + # persist the unsigned installer - store_artifacts: path: build/upload + # add workspace so signing jobs can connect & obtain dmg + - persist_to_workspace: + root: build + # specify path to only include components we want to persist + # accross builds + paths: + - upload + + sign-offline-chat-installer-macos: + macos: + xcode: 14.0.0 + steps: + - checkout + # attach to a workspace containing unsigned dmg + - attach_workspace: + at: build + - run: + name: "Setup Keychain" + command: | + echo $MAC_SIGNING_CERT | base64 --decode > cert.p12 + # cat \<<< "$MAC_SIGNING_CERT" > certs1.pem + # file certs1.pem + # iconv -c -f UTF8 -t ASCII certs1.pem > certs.pem + # openssl pkcs12 -legacy -export -out cert.p12 -in certs.pem -inkey certs.pem -passin pass:"$MAC_SIGNING_CERT_PWD" -passout pass:"$MAC_SIGNING_CERT_PWD" + security create-keychain -p "$MAC_KEYCHAIN_KEY" sign.keychain + security default-keychain -s sign.keychain + security unlock-keychain -p "$MAC_KEYCHAIN_KEY" sign.keychain + security import cert.p12 -k sign.keychain -P "$MAC_SIGNING_CERT_PWD" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MAC_KEYCHAIN_KEY" sign.keychain + rm cert.p12 + - run: + name: "Sign App Bundle" + command: | + python3 -m pip install click + python3 gpt4all-chat/cmake/sign_dmg.py --input-dmg build/upload/gpt4all-installer-darwin.dmg --output-dmg build/upload/gpt4all-installer-darwin-signed.dmg --signing-identity "$MAC_SIGNING_CERT_NAME" + - run: + name: "Sign DMG" + command: | + codesign --options runtime --timestamp -s "$MAC_SIGNING_CERT_NAME" build/upload/gpt4all-installer-darwin-signed.dmg + # add workspace so signing jobs can connect & obtain dmg + - persist_to_workspace: + root: build + # specify path to only include components we want to persist + # accross builds + paths: + - upload + + notarize-offline-chat-installer-macos: + macos: + xcode: 14.0.0 + steps: + - checkout + - attach_workspace: + at: build + # - run: + # name: "Setup Notarize Keychain" + # command: | + # security create-keychain + # sudo xcrun notarytool store-credentials "notarytool-profile" --apple-id "$MAC_NOTARIZATION_ID" --team-id "$MAC_NOTARIZATION_TID" --password "$MAC_NOTARIZATION_KEY" --keychain /Library/Keychains/System.keychain + - run: + name: "Notarize" + command: | + xcrun notarytool submit build/upload/gpt4all-installer-darwin-signed.dmg --apple-id "$MAC_NOTARIZATION_ID" --team-id "$MAC_NOTARIZATION_TID" --password "$MAC_NOTARIZATION_KEY" --wait | tee notarize_log.txt + - run: + name: "Report Notarization Failure" + command: | + NID=`python3 .circleci/grab_notary_id.py notarize_log.txt` && export NID + xcrun notarytool log $NID --keychain-profile "notary-profile" + exit 1 + when: on_fail + # - run: + # name: "Rename and move" + # command: | + # mv build/upload/gpt4all-installer-darwin-signed.dmg build/upload-signed/gpt4all-installer-darwin-signed.dmg + - run: + name: "Staple" + command: | + xcrun stapler staple build/upload/gpt4all-installer-darwin-signed.dmg + - store_artifacts: + path: build/upload + build-offline-chat-installer-linux: machine: image: ubuntu-2204:2023.04.2 @@ -848,6 +930,12 @@ workflows: - build-offline-chat-installer-macos: requires: - hold + - sign-offline-chat-installer-macos: + requires: + - build-offline-chat-installer-macos + - notarize-offline-chat-installer-macos: + requires: + - sign-offline-chat-installer-macos - build-offline-chat-installer-windows: requires: - hold diff --git a/.circleci/grab_notary_id.py b/.circleci/grab_notary_id.py new file mode 100644 index 00000000..947626fa --- /dev/null +++ b/.circleci/grab_notary_id.py @@ -0,0 +1,17 @@ +import re +import sys + +ID_REG = r"id: (.*)" + +def main() -> None: + notary_log = sys.argv[1] + with open(notary_log, "r") as f: + notary_output = f.read() + id_m = re.search(ID_REG, notary_output) + if id_m: + print(id_m.group(1)) + else: + raise RuntimeError("Unable to parse ID from notarization logs") + +if __name__ == "__main__": + main() \ No newline at end of file