diff --git a/.drone.jsonnet b/.drone.jsonnet index eaa616f7b..f12459fe8 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -1,7 +1,17 @@ +// Log a bunch of version information to make it easier for debugging +local version_info = { + name: 'Version Information', + commands: [ + 'git --version', + 'pod --version', + 'xcodebuild -version' + ] +}; + // Intentionally doing a depth of 2 as libSession-util has it's own submodules (and libLokinet likely will as well) local clone_submodules = { name: 'Clone Submodules', - commands: ['git fetch --tags', 'git submodule update --init --recursive --depth=2'] + commands: ['git fetch --tags', 'git submodule update --init --recursive --depth=2 --jobs=4'] }; // cmake options for static deps mirror @@ -24,7 +34,10 @@ local install_cocoapods = { name: 'Install CocoaPods', commands: [' LANG=en_US.UTF-8 pod install || rm -rf ./Pods && LANG=en_US.UTF-8 pod install - '] + '], + depends_on: [ + 'Load CocoaPods Cache' + ] }; // Load from the cached CocoaPods directory (to speed up the build) @@ -49,11 +62,14 @@ local load_cocoapods_cache = { fi |||, 'rm -f /Users/drone/.cocoapods_cache.lock' + ], + depends_on: [ + 'Clone Submodules' ] }; // Override the cached CocoaPods directory (to speed up the next build) -local update_cocoapods_cache = { +local update_cocoapods_cache(depends_on) = { name: 'Update CocoaPods Cache', commands: [ ||| @@ -75,70 +91,99 @@ local update_cocoapods_cache = { fi |||, 'rm -f /Users/drone/.cocoapods_cache.lock' - ] + ], + depends_on: depends_on, +}; + +// Run specified unit tests +local run_tests(testName, testBuildStepName) = { + name: 'Run ' + testName, + commands: [ + 'NSUnbufferedIO=YES set -o pipefail && xcodebuild test-without-building -workspace Session.xcworkspace -scheme Session -derivedDataPath ./build/derivedData -destination "platform=iOS Simulator,name=iPhone 14" -test-timeouts-enabled YES -maximum-test-execution-time-allowance 10 -only-testing ' + testName + ' -collect-test-diagnostics never 2>&1 | xcbeautify --is-ci', + ], + depends_on: [ + testBuildStepName + ], }; [ - // Unit tests + // Unit tests (PRs only) { kind: 'pipeline', type: 'exec', name: 'Unit Tests', platform: { os: 'darwin', arch: 'amd64' }, + trigger: { event: { exclude: [ 'push' ] } }, steps: [ - machine_info, + version_info, clone_submodules, load_cocoapods_cache, install_cocoapods, { - name: 'Run Unit Tests', + name: 'Reset Simulators', + commands: [ + 'xcrun simctl shutdown all', + 'xcrun simctl erase all' + ], + depends_on: [ + 'Install CocoaPods' + ] + }, + { + name: 'Build For Testing', commands: [ 'mkdir build', - 'NSUnbufferedIO=YES set -o pipefail && xcodebuild test -workspace Session.xcworkspace -scheme Session -derivedDataPath ./build/derivedData -destination "platform=iOS Simulator,name=iPhone 14" -destination "platform=iOS Simulator,name=iPhone 14 Pro Max" -parallel-testing-enabled YES -test-timeouts-enabled YES -maximum-test-execution-time-allowance 2 -collect-test-diagnostics never 2>&1 | ./Pods/xcbeautify/xcbeautify --is-ci --report junit --report-path ./build/reports --junit-report-filename junit2.xml' + 'xcodebuild build-for-testing -workspace Session.xcworkspace -scheme Session -derivedDataPath ./build/derivedData -parallelizeTargets -destination "platform=iOS Simulator,name=iPhone 14" | xcbeautify --is-ci', + ], + depends_on: [ + 'Install CocoaPods' ], }, - update_cocoapods_cache + run_tests('SessionTests', 'Build For Testing'), + run_tests('SessionMessagingKitTests', 'Build For Testing'), + run_tests('SessionUtilitiesKitTests', 'Build For Testing'), + { + name: 'Shutdown Simulators', + commands: [ 'xcrun simctl shutdown all' ], + depends_on: [ + 'Build For Testing', + 'Run SessionTests', + 'Run SessionMessagingKitTests', + 'Run SessionUtilitiesKitTests' + ], + when: { + status: ['failure', 'success'] + } + }, + update_cocoapods_cache(['Build For Testing']) ], }, - // Simulator build + // Validate build artifact was created by the direct branch push (PRs only) { kind: 'pipeline', type: 'exec', - name: 'Simulator Build', + name: 'Check Build Artifact Existence', platform: { os: 'darwin', arch: 'amd64' }, - trigger: { event: { exclude: [ 'pull_request' ] } }, + trigger: { event: { exclude: [ 'push' ] } }, steps: [ - machine_info, - clone_submodules, - load_cocoapods_cache, - install_cocoapods, { - name: 'Build', + name: 'Poll for build artifact existence', commands: [ - 'mkdir build', - 'xcodebuild archive -workspace Session.xcworkspace -scheme Session -derivedDataPath ./build/derivedData -configuration "App Store Release" -sdk iphonesimulator -archivePath ./build/Session_sim.xcarchive -destination "generic/platform=iOS Simulator" | ./Pods/xcbeautify/xcbeautify --is-ci' - ], - }, - update_cocoapods_cache, - { - name: 'Upload artifacts', - environment: { SSH_KEY: { from_secret: 'SSH_KEY' } }, - commands: [ - './Scripts/drone-static-upload.sh' + './Scripts/drone-upload-exists.sh' ] - }, - ], + } + ] }, - // AppStore build (generate an archive to be signed later) + // Simulator build (non-PRs only) { kind: 'pipeline', type: 'exec', - name: 'AppStore Build', + name: 'Simulator Build', platform: { os: 'darwin', arch: 'amd64' }, trigger: { event: { exclude: [ 'pull_request' ] } }, steps: [ - machine_info, + version_info, clone_submodules, load_cocoapods_cache, install_cocoapods, @@ -146,17 +191,23 @@ local update_cocoapods_cache = { name: 'Build', commands: [ 'mkdir build', - 'xcodebuild archive -workspace Session.xcworkspace -scheme Session -derivedDataPath ./build/derivedData -configuration "App Store Release" -sdk iphoneos -archivePath ./build/Session.xcarchive -destination "generic/platform=iOS" -allowProvisioningUpdates CODE_SIGNING_ALLOWED=NO | ./Pods/xcbeautify/xcbeautify --is-ci' + 'xcodebuild archive -workspace Session.xcworkspace -scheme Session -derivedDataPath ./build/derivedData -parallelizeTargets -configuration "App Store Release" -sdk iphonesimulator -archivePath ./build/Session_sim.xcarchive -destination "generic/platform=iOS Simulator" | xcbeautify --is-ci' + ], + depends_on: [ + 'Install CocoaPods' ], }, - update_cocoapods_cache, + update_cocoapods_cache(['Build']), { name: 'Upload artifacts', environment: { SSH_KEY: { from_secret: 'SSH_KEY' } }, commands: [ './Scripts/drone-static-upload.sh' + ], + depends_on: [ + 'Build' ] }, ], - }, -] \ No newline at end of file + } +] diff --git a/Podfile b/Podfile index 201db8853..69fbc174f 100644 --- a/Podfile +++ b/Podfile @@ -5,9 +5,6 @@ inhibit_all_warnings! install! 'cocoapods', :warn_for_unused_master_specs_repo => false -# CI Dependencies -pod 'xcbeautify' - # Dependencies to be included in the app and all extensions/frameworks abstract_target 'GlobalDependencies' do # FIXME: If https://github.com/jedisct1/swift-sodium/pull/249 gets resolved then revert this back to the standard pod diff --git a/Podfile.lock b/Podfile.lock index c61862e8f..a6e6da3fc 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -45,7 +45,6 @@ PODS: - SQLCipher/common - SwiftProtobuf (1.5.0) - WebRTC-lib (114.0.0) - - xcbeautify (0.17.0) - YapDatabase/SQLCipher (3.1.1): - YapDatabase/SQLCipher/Core (= 3.1.1) - YapDatabase/SQLCipher/Extensions (= 3.1.1) @@ -128,7 +127,6 @@ DEPENDENCIES: - SQLCipher (~> 4.5.3) - SwiftProtobuf (~> 1.5.0) - WebRTC-lib - - xcbeautify - YapDatabase/SQLCipher (from `https://github.com/oxen-io/session-ios-yap-database.git`, branch `signal-release`) - YYImage/libwebp (from `https://github.com/signalapp/YYImage`) @@ -148,7 +146,6 @@ SPEC REPOS: - SQLCipher - SwiftProtobuf - WebRTC-lib - - xcbeautify EXTERNAL SOURCES: Curve25519Kit: @@ -201,10 +198,9 @@ SPEC CHECKSUMS: SQLCipher: 57fa9f863fa4a3ed9dd3c90ace52315db8c0fdca SwiftProtobuf: 241400280f912735c1e1b9fe675fdd2c6c4d42e2 WebRTC-lib: d83df8976fa608b980f1d85796b3de66d60a1953 - xcbeautify: 6e2f57af5c3a86d490376d5758030a8dcc201c1b YapDatabase: b418a4baa6906e8028748938f9159807fd039af4 YYImage: f1ddd15ac032a58b78bbed1e012b50302d318331 -PODFILE CHECKSUM: dd814a5a92577bb2a94dac6a1cc482f193721cdf +PODFILE CHECKSUM: f56c28baefe3077effcb3a2ea5941b52c4cc6e86 -COCOAPODS: 1.14.3 +COCOAPODS: 1.15.0 diff --git a/Scripts/drone-static-upload.sh b/Scripts/drone-static-upload.sh index 681e58994..1363a4b83 100755 --- a/Scripts/drone-static-upload.sh +++ b/Scripts/drone-static-upload.sh @@ -74,4 +74,4 @@ SFTP set +o xtrace -echo -e "\n\n\n\n\e[32;1mUploaded to https://${upload_to}/${archive}\e[0m\n\n\n" \ No newline at end of file +echo -e "\n\n\n\n\e[32;1mUploaded to https://${upload_to}/${archive}\e[0m\n\n\n" diff --git a/Scripts/drone-upload-exists.sh b/Scripts/drone-upload-exists.sh new file mode 100755 index 000000000..a810b77a3 --- /dev/null +++ b/Scripts/drone-upload-exists.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# +# Script used with Drone CI to check for the existence of a build artifact. + +if [[ -z ${DRONE_REPO} || -z ${DRONE_PULL_REQUEST} ]]; then + echo -e "\n\n\n\n\e[31;1mRequired env variables not specified, likely a tag build so just failing\e[0m\n\n\n" + exit 1 +fi + +# This file info MUST match the structure of `base` in the `drone-static-upload.sh` script in +# order to function correctly +prefix="session-ios-" +suffix="-${DRONE_COMMIT:0:9}-sim.tar.xz" + +# Extracting head.label using string manipulation +echo "Extracting repo information for 'https://api.github.com/repos/${DRONE_REPO}/pulls/${DRONE_PULL_REQUEST}'" +pr_info=$(curl -s https://api.github.com/repos/${DRONE_REPO}/pulls/${DRONE_PULL_REQUEST}) +pr_info_clean=$(echo "$pr_info" | tr -d '[:space:]') +head_info=$(echo "$pr_info_clean" | sed -n 's/.*"head"\(.*\)"base".*/\1/p') +fork_repo=$(echo "$head_info" | grep -o '"full_name":"[^"]*' | sed 's/"full_name":"//') +fork_branch=$(echo "$head_info" | grep -o '"ref":"[^"]*' | sed 's/"ref":"//') +upload_dir="https://oxen.rocks/${fork_repo}/${fork_branch}" + +echo "Starting to poll ${upload_dir}/ every 10s to check for a build matching '${prefix}.*${suffix}'" + +# Loop indefinitely the CI can timeout the script if it takes too long +total_poll_duration=0 +max_poll_duration=$((30 * 60)) # Poll for a maximum of 30 mins + +while true; do + # Need to add the trailing '/' or else we get a '301' response + build_artifacts_html=$(curl -s "${upload_dir}/") + + if [ $? != 0 ]; then + echo -e "\n\n\n\n\e[31;1mFailed to retrieve build artifact list\e[0m\n\n\n" + exit 1 + fi + + # Extract 'session-ios...' titles using grep and awk then look for the target file + current_build_artifacts=$(echo "$build_artifacts_html" | grep -o "href=\"${prefix}[^\"]*" | sed 's/href="//') + target_file=$(echo "$current_build_artifacts" | grep -o "${prefix}.*${suffix}" | tail -n 1) + + if [ -n "$target_file" ]; then + echo -e "\n\n\n\n\e[32;1mExisting build artifact at ${upload_dir}/${target_file}\e[0m\n\n\n" + exit 0 + fi + + # Sleep for 10 seconds before checking again + sleep 10 + total_poll_duration=$((total_poll_duration + 10)) + + if [ $total_poll_duration -gt $max_poll_duration ]; then + echo -e "\n\n\n\n\e[31;1mCould not find existing build artifact after polling for 30 minutes\e[0m\n\n\n" + exit 1 + fi +done