diff --git a/.github/workflows/macos-build.yaml b/.github/workflows/macos-build.yaml index 22b0274..2139c83 100644 --- a/.github/workflows/macos-build.yaml +++ b/.github/workflows/macos-build.yaml @@ -100,6 +100,13 @@ jobs: - name: Get version from git tag run: echo "GIT_DESCRIBE=$(git describe --always --dirty)" >> $GITHUB_ENV + - name: Prepare keychain + env: + OPENEMV_MACOS_CERT_BASE64: ${{ secrets.OPENEMV_MACOS_CERT_BASE64 }} + OPENEMV_MACOS_CERT_PWD: ${{ secrets.OPENEMV_MACOS_CERT_PWD }} + KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} + run: scripts/prepare_macos_keychain.sh + - name: Configure CMake run: | cmake -B build \ @@ -108,7 +115,8 @@ jobs: -DFETCH_ARGP=YES \ -DBUILD_EMV_TOOL=YES \ -DBUILD_EMV_VIEWER=YES \ - -DBUILD_MACOSX_BUNDLE=YES + -DBUILD_MACOSX_BUNDLE=YES \ + -DSIGN_MACOSX_BUNDLE=openemv.org - name: Build run: cmake --build build @@ -119,6 +127,10 @@ jobs: - name: Package run: cmake --build build --target package + - name: Clean up keychain + if: ${{ always() }} + run: scripts/cleanup_macos_keychain.sh + - name: Upload artifact uses: actions/upload-artifact@v4 with: diff --git a/scripts/cleanup_macos_keychain.sh b/scripts/cleanup_macos_keychain.sh new file mode 100755 index 0000000..8ec2c1e --- /dev/null +++ b/scripts/cleanup_macos_keychain.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# This script implements the cleanup recommended by: +# https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development + +# Remove temporary keychain +security delete-keychain $RUNNER_TEMP/app-signing.keychain-db + diff --git a/scripts/prepare_macos_keychain.sh b/scripts/prepare_macos_keychain.sh new file mode 100755 index 0000000..02663ce --- /dev/null +++ b/scripts/prepare_macos_keychain.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# This script is inspired by these guides: +# - https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development +# - https://localazy.com/blog/how-to-automatically-sign-macos-apps-using-github-actions +# - https://federicoterzi.com/blog/automatic-code-signing-and-notarization-for-macos-apps-using-github-actions/ +# - https://github.com/lando/code-sign-action/ + +# This script assumes that these environment variables are provided: +# - RUNNER_TEMP (temporary directory provided by Github Actions runner) +# - OPENEMV_MACOS_CERT_BASE64 +# - OPENEMV_MACOS_CERT_PWD +# - KEYCHAIN_PASSWORD + +# Temporary paths +OPENEMV_MACOS_CERT_PATH=$RUNNER_TEMP/certificate.p12 +KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + +# Create temporary keychain +security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH +security set-keychain-settings -lut 21600 $KEYCHAIN_PATH +security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + +# Decode and import signing certificate +echo -n "$OPENEMV_MACOS_CERT_BASE64" | base64 --decode > $OPENEMV_MACOS_CERT_PATH +security import $OPENEMV_MACOS_CERT_PATH -P "$OPENEMV_MACOS_CERT_PWD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH +security list-keychains -d user -s $KEYCHAIN_PATH +security find-identity -v -p codesigning + +# Allow codesign application to use signing key +security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + +# Cleanup +rm $OPENEMV_MACOS_CERT_PATH diff --git a/viewer/CMakeLists.txt b/viewer/CMakeLists.txt index e39c8d8..074cc36 100644 --- a/viewer/CMakeLists.txt +++ b/viewer/CMakeLists.txt @@ -252,3 +252,37 @@ install( COMPONENT emv_viewer_bundle DESTINATION . # Install to root of MacOS bundle ) + +# Sign bundle using the specified identity +option(SIGN_MACOSX_BUNDLE "Sign MacOS bundle using the specified identity (use - for ad-hoc signing)") +if(APPLE AND BUILD_MACOSX_BUNDLE AND SIGN_MACOSX_BUNDLE) + find_program(CODESIGN_EXECUTABLE codesign) + if(NOT CODESIGN_EXECUTABLE) + message(FATAL_ERROR "codesign not found") + endif() + + # When using install(CODE) instead of CPACK_PRE_BUILD_SCRIPTS to sign + # the bundle, it must always be the last install() command to ensure + # that all of the bundle files are already present + install(CODE + " + if(TARGET emv-decode) + message(STATUS \"Using identity '${SIGN_MACOSX_BUNDLE}' to sign binary at \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\$/bin/emv-decode\") + execute_process(COMMAND ${CODESIGN_EXECUTABLE} --sign \"${SIGN_MACOSX_BUNDLE}\" --deep \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\$/bin/emv-decode\") + execute_process(COMMAND ${CODESIGN_EXECUTABLE} --display --verbose --verbose=4 \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\$/bin/emv-decode\") + execute_process(COMMAND ${CODESIGN_EXECUTABLE} --verify --verbose --deep \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\$/bin/emv-decode\") + endif() + if(TARGET emv-tool) + message(STATUS \"Using identity '${SIGN_MACOSX_BUNDLE}' to sign binary at \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\$/bin/emv-tool\") + execute_process(COMMAND ${CODESIGN_EXECUTABLE} --sign \"${SIGN_MACOSX_BUNDLE}\" --deep \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\$/bin/emv-tool\") + execute_process(COMMAND ${CODESIGN_EXECUTABLE} --display --verbose --verbose=4 \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\$/bin/emv-tool\") + execute_process(COMMAND ${CODESIGN_EXECUTABLE} --verify --verbose --deep \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\$/bin/emv-tool\") + endif() + message(STATUS \"Using identity '${SIGN_MACOSX_BUNDLE}' to sign bundle at \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\$\") + execute_process(COMMAND ${CODESIGN_EXECUTABLE} --sign \"${SIGN_MACOSX_BUNDLE}\" --deep \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\$\") + execute_process(COMMAND ${CODESIGN_EXECUTABLE} --display --verbose --verbose=4 \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\$\") + execute_process(COMMAND ${CODESIGN_EXECUTABLE} --verify --verbose --deep \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/\$\") + " + COMPONENT emv_viewer_bundle + ) +endif()