From 8ee1fe8379854bb2ff70502d76ef5396210a8706 Mon Sep 17 00:00:00 2001 From: Nan Date: Tue, 2 Jun 2026 17:49:49 -0700 Subject: [PATCH 1/3] fix: sign release XCFrameworks with the OneSignal identity, archive unsigned Archive with code signing disabled so the build needs no developer certificate, then sign each inner slice and the wrapper with the OneSignal Apple Distribution identity. Move the framework list into one shared env var and drop the unused dev-cert import. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/create-release-prs.yml | 48 ++++++++++++-------- iOS_SDK/OneSignalSDK/build_all_frameworks.sh | 12 +++-- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/.github/workflows/create-release-prs.yml b/.github/workflows/create-release-prs.yml index 1e740dcd8..b1f83068d 100644 --- a/.github/workflows/create-release-prs.yml +++ b/.github/workflows/create-release-prs.yml @@ -44,6 +44,20 @@ jobs: BASE_BRANCH: ${{ github.event.inputs.base_branch }} RELEASE_BRANCH: ${{ needs.prep.outputs.release_branch }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Single source of truth for the XCFrameworks the Code Sign and Verify steps + # operate on (paths relative to iOS_SDK/OneSignalSDK). Keep in sync with the + # frameworks built by build_all_frameworks.sh. + XCFRAMEWORKS: >- + OneSignal_Core/OneSignalCore.xcframework + OneSignal_OSCore/OneSignalOSCore.xcframework + OneSignal_Outcomes/OneSignalOutcomes.xcframework + OneSignal_Extension/OneSignalExtension.xcframework + OneSignal_Notifications/OneSignalNotifications.xcframework + OneSignal_User/OneSignalUser.xcframework + OneSignal_LiveActivities/OneSignalLiveActivities.xcframework + OneSignal_Location/OneSignalLocation.xcframework + OneSignal_InAppMessages/OneSignalInAppMessages.xcframework + OneSignal_XCFramework/OneSignalFramework.xcframework steps: - name: 📋 Display Configuration @@ -75,21 +89,13 @@ jobs: with: xcode-version: "15.2" - - name: Install the Apple distribution certificate and provisioning profile (OneSignal) + - name: Install the Apple distribution certificate (OneSignal) uses: apple-actions/import-codesign-certs@v2 with: keychain-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }} p12-file-base64: ${{ secrets.CERTIFICATES_P12 }} p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }} - - name: Install the Apple distribution certificate and provisioning profile (Lilomi) - uses: apple-actions/import-codesign-certs@v2 - with: - create-keychain: false # do not create a new keychain for this value - keychain-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }} - p12-file-base64: ${{ secrets.DEV_CERTIFICATES_P12 }} - p12-password: ${{ secrets.DEV_CERTIFICATES_P12_PASSWORD }} - - name: Update Version in SDK and Podspec Files run: | cd iOS_SDK/OneSignalSDK @@ -112,16 +118,20 @@ jobs: - name: Code Sign run: | cd iOS_SDK/OneSignalSDK - codesign --timestamp -v --sign "Apple Distribution: OneSignal, Inc. (J3J28YJX9L)" OneSignal_Core/OneSignalCore.xcframework - codesign --timestamp -v --sign "Apple Distribution: OneSignal, Inc. (J3J28YJX9L)" OneSignal_Extension/OneSignalExtension.xcframework - codesign --timestamp -v --sign "Apple Distribution: OneSignal, Inc. (J3J28YJX9L)" OneSignal_InAppMessages/OneSignalInAppMessages.xcframework - codesign --timestamp -v --sign "Apple Distribution: OneSignal, Inc. (J3J28YJX9L)" OneSignal_Location/OneSignalLocation.xcframework - codesign --timestamp -v --sign "Apple Distribution: OneSignal, Inc. (J3J28YJX9L)" OneSignal_Notifications/OneSignalNotifications.xcframework - codesign --timestamp -v --sign "Apple Distribution: OneSignal, Inc. (J3J28YJX9L)" OneSignal_OSCore/OneSignalOSCore.xcframework - codesign --timestamp -v --sign "Apple Distribution: OneSignal, Inc. (J3J28YJX9L)" OneSignal_Outcomes/OneSignalOutcomes.xcframework - codesign --timestamp -v --sign "Apple Distribution: OneSignal, Inc. (J3J28YJX9L)" OneSignal_User/OneSignalUser.xcframework - codesign --timestamp -v --sign "Apple Distribution: OneSignal, Inc. (J3J28YJX9L)" OneSignal_XCFramework/OneSignalFramework.xcframework - codesign --timestamp -v --sign "Apple Distribution: OneSignal, Inc. (J3J28YJX9L)" OneSignal_LiveActivities/OneSignalLiveActivities.xcframework + set -e + IDENTITY="Apple Distribution: OneSignal, Inc. (J3J28YJX9L)" + for XCF in $XCFRAMEWORKS; do + # Sign inside-out: each slice's .framework binary first, then the + # .xcframework wrapper. -f replaces the linker's ad-hoc signature left + # by the unsigned archive build, so both inner and outer end up signed + # with the official OneSignal Apple Distribution identity. + while IFS= read -r FW; do + echo "Signing inner framework: $FW" + codesign --timestamp -f -v --sign "$IDENTITY" "$FW" + done < <(find "$XCF" -type d -name "*.framework") + echo "Signing wrapper: $XCF" + codesign --timestamp -f -v --sign "$IDENTITY" "$XCF" + done shell: bash - name: Commit Build and Push Changes diff --git a/iOS_SDK/OneSignalSDK/build_all_frameworks.sh b/iOS_SDK/OneSignalSDK/build_all_frameworks.sh index c9b7804ed..936c78d1c 100755 --- a/iOS_SDK/OneSignalSDK/build_all_frameworks.sh +++ b/iOS_SDK/OneSignalSDK/build_all_frameworks.sh @@ -26,11 +26,17 @@ create_xcframework() { xcodebuild -list - xcodebuild archive ONLY_ACTIVE_ARCH=NO -scheme ${BUILD_SCHEME} -destination="generic/platform=iOS Simulator" -archivePath "${SIMULATOR_ARCHIVE_PATH}" -sdk iphonesimulator SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES + # Note: code signing is disabled here. These are distribution XCFrameworks + # that are signed afterward with the Apple Distribution cert in the workflow's + # "Code Sign" step, so the archive itself does not need to be signed. This also + # avoids depending on the project's baked-in DEVELOPMENT_TEAM / dev certificate. + CODE_SIGN_FLAGS="CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO" - xcodebuild archive -scheme ${BUILD_SCHEME} -destination="generic/platform=iOS" -archivePath "${IOS_DEVICE_ARCHIVE_PATH}" -sdk iphoneos SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES + xcodebuild archive ONLY_ACTIVE_ARCH=NO -scheme ${BUILD_SCHEME} -destination="generic/platform=iOS Simulator" -archivePath "${SIMULATOR_ARCHIVE_PATH}" -sdk iphonesimulator SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES ${CODE_SIGN_FLAGS} - xcodebuild archive -scheme ${BUILD_SCHEME} -destination='generic/platform=macOS,variant=Mac Catalyst' -archivePath "${CATALYST_ARCHIVE_PATH}" SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES + xcodebuild archive -scheme ${BUILD_SCHEME} -destination="generic/platform=iOS" -archivePath "${IOS_DEVICE_ARCHIVE_PATH}" -sdk iphoneos SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES ${CODE_SIGN_FLAGS} + + xcodebuild archive -scheme ${BUILD_SCHEME} -destination='generic/platform=macOS,variant=Mac Catalyst' -archivePath "${CATALYST_ARCHIVE_PATH}" SKIP_INSTALL=NO BUILD_LIBRARIES_FOR_DISTRIBUTION=YES ${CODE_SIGN_FLAGS} xcodebuild -create-xcframework -framework ${SIMULATOR_ARCHIVE_PATH}/Products/Library/Frameworks/${FRAMEWORK_NAME}.framework -debug-symbols ${SIMULATOR_ARCHIVE_PATH}/dSYMs/${FRAMEWORK_NAME}.framework.dSYM -framework ${IOS_DEVICE_ARCHIVE_PATH}/Products/Library/Frameworks/${FRAMEWORK_NAME}.framework -debug-symbols ${IOS_DEVICE_ARCHIVE_PATH}/dSYMs/${FRAMEWORK_NAME}.framework.dSYM -framework ${CATALYST_ARCHIVE_PATH}/Products/Library/Frameworks/${FRAMEWORK_NAME}.framework -debug-symbols ${CATALYST_ARCHIVE_PATH}/dSYMs/${FRAMEWORK_NAME}.framework.dSYM -output "${FRAMEWORK_PATH}" rm -rf "${SIMULATOR_ARCHIVE_PATH}" From 309b1ffbcbd2dc6ce3ed000e7e0b3098f694cc32 Mon Sep 17 00:00:00 2001 From: Nan Date: Tue, 2 Jun 2026 17:50:02 -0700 Subject: [PATCH 2/3] ci: verify XCFramework signatures after signing Run codesign --verify on every slice and wrapper and assert the OneSignal signer, so a missed slice or wrong identity fails the release. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/create-release-prs.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/create-release-prs.yml b/.github/workflows/create-release-prs.yml index b1f83068d..be72e6b50 100644 --- a/.github/workflows/create-release-prs.yml +++ b/.github/workflows/create-release-prs.yml @@ -134,6 +134,27 @@ jobs: done shell: bash + - name: Verify Code Signing + run: | + cd iOS_SDK/OneSignalSDK + set -e + # Fail the release if any slice or wrapper is tampered/unsigned or not + # signed by the OneSignal team. --deep is unreliable across xcframework + # slices, so each inner .framework is verified explicitly. + REQUIREMENT="anchor apple generic and certificate leaf[subject.OU] = J3J28YJX9L" + for XCF in $XCFRAMEWORKS; do + while IFS= read -r FW; do + echo "Verifying inner framework: $FW" + codesign --verify --strict --verbose=2 "$FW" + codesign --verify -R="$REQUIREMENT" "$FW" + done < <(find "$XCF" -type d -name "*.framework") + echo "Verifying wrapper: $XCF" + codesign --verify --strict --verbose=2 "$XCF" + codesign --verify -R="$REQUIREMENT" "$XCF" + done + echo "All XCFrameworks verified: intact and signed by OneSignal (J3J28YJX9L)." + shell: bash + - name: Commit Build and Push Changes run: | git commit -am "chore: build binaries" From f9bf7f63dcc73f49106187c784a1d1196e9dccdf Mon Sep 17 00:00:00 2001 From: Nan Date: Wed, 10 Jun 2026 19:07:54 -0700 Subject: [PATCH 3/3] fix: update Package.swift binary targets by name, not line number Editing by hardcoded line numbers silently corrupted the manifest whenever unrelated lines shifted. Locate each binary target by its framework name and rewrite the url version plus the checksum on the line immediately after it, so a reordered or renamed checksum line can't overwrite a neighbour. Require a version argument and fail loudly on any mismatch rather than corrupting the manifest silently. Co-Authored-By: Claude Opus 4.8 --- iOS_SDK/OneSignalSDK/update_swift_package.sh | 76 +++++++++++++++----- 1 file changed, 59 insertions(+), 17 deletions(-) diff --git a/iOS_SDK/OneSignalSDK/update_swift_package.sh b/iOS_SDK/OneSignalSDK/update_swift_package.sh index b62ca7913..cc135a3d5 100755 --- a/iOS_SDK/OneSignalSDK/update_swift_package.sh +++ b/iOS_SDK/OneSignalSDK/update_swift_package.sh @@ -8,6 +8,11 @@ WORKING_DIR=$(pwd) # read VERSION_NUMBER VERSION_NUMBER=$1 +if [ -z "${VERSION_NUMBER}" ]; then + echo "ERROR: release version argument is required (usage: $0 )" >&2 + exit 1 +fi + update_framework() { FRAMEWORK_FOLDER_NAME=$1 @@ -36,43 +41,80 @@ update_framework() { # Compute the checksum for the Zipped framework echo "Computing package checksum and updating Package.swift ${SWIFT_PACKAGE_PATH}" CHECKSUM=$(swift package compute-checksum "${FRAMEWORK_ZIP_PATH}") - SWIFT_PM_CHECKSUM_LINE=" checksum: \"${CHECKSUM}\"" - - # Use sed to remove line from the Swift.package and replace it with the new checksum - sed -i '' "$3s/.*/$SWIFT_PM_CHECKSUM_LINE/" "${SWIFT_PACKAGE_PATH}" - SWIFT_PM_URL_LINE=" url: \"https:\/\/github.com\/OneSignal\/OneSignal-iOS-SDK\/releases\/download\/${VERSION_NUMBER}\/${FRAMEWORK_NAME}.xcframework.zip\"," - #Use sed to remove line from the Swift.package and replace it with the new URL for the new release - sed -i '' "$4s/.*/$SWIFT_PM_URL_LINE/" "${SWIFT_PACKAGE_PATH}" + if [ -z "${CHECKSUM}" ]; then + echo "ERROR: empty checksum computed for ${FRAMEWORK_NAME}" >&2 + exit 1 + fi + # Update this framework's .binaryTarget in place, located by the framework + # name in its url line (NOT by hardcoded line number). Only the version in the + # url and the hash in the following checksum line are rewritten; surrounding + # structure/formatting is preserved. Matching by name keeps this correct even + # when the manifest layout shifts -- the previous line-number approach silently + # corrupted Package.swift once unrelated lines moved (broke 5.5.2 SPM). + awk -v fw="${FRAMEWORK_NAME}" -v ver="${VERSION_NUMBER}" -v chk="${CHECKSUM}" ' + # This framework'"'"'s url line: bump only the version path segment. + $0 ~ ("url: \"https://github.com/OneSignal/OneSignal-iOS-SDK/releases/download/[^/]*/" fw "\\.xcframework\\.zip\"") { + sub("download/[^/]*/", "download/" ver "/") + print + expect_checksum = 1 + next + } + # The checksum must sit on the line IMMEDIATELY after that url (the + # well-formed .binaryTarget layout). Only that one line is eligible, so a + # reordered/stray/renamed checksum line can never be clobbered: if it is not + # a checksum line nothing is rewritten and the post-run guard fails loudly. + expect_checksum { + expect_checksum = 0 + if ($0 ~ /^[[:space:]]*checksum: "[0-9a-fA-F]*"[[:space:]]*$/) { + sub("checksum: \"[0-9a-fA-F]*\"", "checksum: \"" chk "\"") + } + print + next + } + { print } + ' "${SWIFT_PACKAGE_PATH}" > "${SWIFT_PACKAGE_PATH}.tmp" + mv "${SWIFT_PACKAGE_PATH}.tmp" "${SWIFT_PACKAGE_PATH}" + + # Fail loudly if the entry was not found/updated, instead of silently leaving + # the manifest stale or malformed. + if ! grep -q "releases/download/${VERSION_NUMBER}/${FRAMEWORK_NAME}.xcframework.zip" "${SWIFT_PACKAGE_PATH}"; then + echo "ERROR: could not find/update url for ${FRAMEWORK_NAME} in Package.swift" >&2 + exit 1 + fi + if ! grep -q "checksum: \"${CHECKSUM}\"" "${SWIFT_PACKAGE_PATH}"; then + echo "ERROR: could not update checksum for ${FRAMEWORK_NAME} in Package.swift" >&2 + exit 1 + fi #Open XCFramework folder to drag zip into new release open "${WORKING_DIR}/${FRAMEWORK_FOLDER_NAME}" } ## OneSignal LiveActivities ## -update_framework "OneSignal_LiveActivities" "OneSignalLiveActivities" "163" "162" +update_framework "OneSignal_LiveActivities" "OneSignalLiveActivities" ## OneSignal Core ## -update_framework "OneSignal_Core" "OneSignalCore" "158" "157" +update_framework "OneSignal_Core" "OneSignalCore" ## OneSignal OSCore ## -update_framework "OneSignal_OSCore" "OneSignalOSCore" "153" "152" +update_framework "OneSignal_OSCore" "OneSignalOSCore" ## OneSignal Outcomes ## -update_framework "OneSignal_Outcomes" "OneSignalOutcomes" "148" "147" +update_framework "OneSignal_Outcomes" "OneSignalOutcomes" ## OneSignal Extension ## -update_framework "OneSignal_Extension" "OneSignalExtension" "143" "142" +update_framework "OneSignal_Extension" "OneSignalExtension" ## OneSignal Notifications ## -update_framework "OneSignal_Notifications" "OneSignalNotifications" "138" "137" +update_framework "OneSignal_Notifications" "OneSignalNotifications" ## OneSignal User ## -update_framework "OneSignal_User" "OneSignalUser" "133" "132" +update_framework "OneSignal_User" "OneSignalUser" ## OneSignal Location ## -update_framework "OneSignal_Location" "OneSignalLocation" "128" "127" +update_framework "OneSignal_Location" "OneSignalLocation" ## OneSignal InAppMessages ## -update_framework "OneSignal_InAppMessages" "OneSignalInAppMessages" "123" "122" +update_framework "OneSignal_InAppMessages" "OneSignalInAppMessages" ## OneSignal ## -update_framework "OneSignal_XCFramework" "OneSignalFramework" "118" "117" +update_framework "OneSignal_XCFramework" "OneSignalFramework"