From 0d6aae486e7158b6a7ab176aa7d93afe77eff12a Mon Sep 17 00:00:00 2001 From: Thomas Goyne Date: Tue, 10 Sep 2024 09:05:32 -0700 Subject: [PATCH] Run pull request tests on Github Actions And remove all Xcode Cloud related things. --- .github/workflows/build-pr.yml | 469 ++++++++++++++ .../update-xcode-cloud-workflows.yml | 16 - RealmSwift/Tests/ObjectTests.swift | 7 +- build.sh | 65 +- ci_scripts/ci_post_clone.sh | 96 --- ci_scripts/ci_pre_xcodebuild.sh | 11 - dependencies.list | 2 +- .../installation/SubRealm/SubRealm.podspec | 1 + scripts/pr-ci-matrix.rb | 223 +++---- scripts/xcode_cloud_helper.rb | 603 ------------------ 10 files changed, 599 insertions(+), 894 deletions(-) create mode 100644 .github/workflows/build-pr.yml delete mode 100644 .github/workflows/update-xcode-cloud-workflows.yml delete mode 100755 ci_scripts/ci_post_clone.sh delete mode 100755 ci_scripts/ci_pre_xcodebuild.sh delete mode 100755 scripts/xcode_cloud_helper.rb diff --git a/.github/workflows/build-pr.yml b/.github/workflows/build-pr.yml new file mode 100644 index 0000000000..72b3943cbd --- /dev/null +++ b/.github/workflows/build-pr.yml @@ -0,0 +1,469 @@ + +# This is a generated file produced by scripts/pr-ci-matrix.rb. +name: Pull request build and test +on: + pull_request: + paths-ignore: + - '**.md' + workflow_dispatch: + +jobs: + docs: + runs-on: macos-14 + name: Test docs + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - run: sudo xcode-select -switch /Applications/Xcode_15.4.app + - run: bundle exec sh build.sh verify-docs + swiftlint: + runs-on: macos-14 + name: Check swiftlint + steps: + - uses: actions/checkout@v4 + - run: sudo xcode-select -switch /Applications/Xcode_15.4.app + - run: brew install swiftlint + - run: sh build.sh verify-swiftlint + + osx-15_3: + runs-on: macos-14 + name: Test osx on Xcode 15.3 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-osx + + osx-15_4: + runs-on: macos-14 + name: Test osx on Xcode 15.4 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.4.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-osx + + osx-16_beta_6: + runs-on: macos-14 + name: Test osx on Xcode 16_beta_6 + env: + DEVELOPER_DIR: '/Applications/Xcode_16_beta_6.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-osx + + osx-16_1_beta: + runs-on: macos-14 + name: Test osx on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-osx + + osx-encryption-16_1_beta: + runs-on: macos-14 + name: Test osx-encryption on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-osx-encryption + + swiftpm-15_3: + runs-on: macos-14 + name: Test swiftpm on Xcode 15.3 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-swiftpm + + swiftpm-16_1_beta: + runs-on: macos-14 + name: Test swiftpm on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-swiftpm + + swiftpm-debug-15_3: + runs-on: macos-14 + name: Test swiftpm-debug on Xcode 15.3 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-swiftpm-debug + + swiftpm-debug-15_4: + runs-on: macos-14 + name: Test swiftpm-debug on Xcode 15.4 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.4.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-swiftpm-debug + + swiftpm-debug-16_beta_6: + runs-on: macos-14 + name: Test swiftpm-debug on Xcode 16_beta_6 + env: + DEVELOPER_DIR: '/Applications/Xcode_16_beta_6.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-swiftpm-debug + + swiftpm-debug-16_1_beta: + runs-on: macos-14 + name: Test swiftpm-debug on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-swiftpm-debug + + swiftpm-address-16_1_beta: + runs-on: macos-14 + name: Test swiftpm-address on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-swiftpm-address + + swiftpm-thread-16_1_beta: + runs-on: macos-14 + name: Test swiftpm-thread on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-swiftpm-thread + + ios-static-15_3: + runs-on: macos-14 + name: Test ios-static on Xcode 15.3 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-ios-static + + ios-static-16_1_beta: + runs-on: macos-14 + name: Test ios-static on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-ios-static + + ios-15_3: + runs-on: macos-14 + name: Test ios on Xcode 15.3 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-ios + + ios-16_1_beta: + runs-on: macos-14 + name: Test ios on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-ios + + watchos-15_3: + runs-on: macos-14 + name: Test watchos on Xcode 15.3 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-watchos + + watchos-16_1_beta: + runs-on: macos-14 + name: Test watchos on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-watchos + + tvos-15_3: + runs-on: macos-14 + name: Test tvos on Xcode 15.3 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-tvos + + tvos-16_1_beta: + runs-on: macos-14 + name: Test tvos on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-tvos + + visionos-15_3: + runs-on: macos-14 + name: Test visionos on Xcode 15.3 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-visionos + + visionos-16_1_beta: + runs-on: macos-14 + name: Test visionos on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-visionos + + osx-swift-15_3: + runs-on: macos-14 + name: Test osx-swift on Xcode 15.3 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-osx-swift + + osx-swift-15_4: + runs-on: macos-14 + name: Test osx-swift on Xcode 15.4 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.4.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-osx-swift + + osx-swift-16_beta_6: + runs-on: macos-14 + name: Test osx-swift on Xcode 16_beta_6 + env: + DEVELOPER_DIR: '/Applications/Xcode_16_beta_6.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-osx-swift + + osx-swift-16_1_beta: + runs-on: macos-14 + name: Test osx-swift on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-osx-swift + + ios-swift-15_3: + runs-on: macos-14 + name: Test ios-swift on Xcode 15.3 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-ios-swift + + ios-swift-16_1_beta: + runs-on: macos-14 + name: Test ios-swift on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-ios-swift + + tvos-swift-15_3: + runs-on: macos-14 + name: Test tvos-swift on Xcode 15.3 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-tvos-swift + + tvos-swift-16_1_beta: + runs-on: macos-14 + name: Test tvos-swift on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-tvos-swift + + osx-swift-evolution-16_1_beta: + runs-on: macos-14 + name: Test osx-swift-evolution on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-osx-swift-evolution + + ios-swift-evolution-16_1_beta: + runs-on: macos-14 + name: Test ios-swift-evolution on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-ios-swift-evolution + + tvos-swift-evolution-16_1_beta: + runs-on: macos-14 + name: Test tvos-swift-evolution on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-tvos-swift-evolution + + catalyst-15_3: + runs-on: macos-14 + name: Test catalyst on Xcode 15.3 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-catalyst + + catalyst-16_1_beta: + runs-on: macos-14 + name: Test catalyst on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-catalyst + + catalyst-swift-15_3: + runs-on: macos-14 + name: Test catalyst-swift on Xcode 15.3 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-catalyst-swift + + catalyst-swift-16_1_beta: + runs-on: macos-14 + name: Test catalyst-swift on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-catalyst-swift + + xcframework-16_1_beta: + runs-on: macos-14 + name: Test xcframework on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-xcframework + + cocoapods-osx-15_3: + runs-on: macos-14 + name: Test cocoapods-osx on Xcode 15.3 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.3.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-cocoapods-osx + + cocoapods-osx-15_4: + runs-on: macos-14 + name: Test cocoapods-osx on Xcode 15.4 + env: + DEVELOPER_DIR: '/Applications/Xcode_15.4.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-cocoapods-osx + + cocoapods-osx-16_beta_6: + runs-on: macos-14 + name: Test cocoapods-osx on Xcode 16_beta_6 + env: + DEVELOPER_DIR: '/Applications/Xcode_16_beta_6.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-cocoapods-osx + + cocoapods-osx-16_1_beta: + runs-on: macos-14 + name: Test cocoapods-osx on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-cocoapods-osx + + cocoapods-ios-static-16_1_beta: + runs-on: macos-14 + name: Test cocoapods-ios-static on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-cocoapods-ios-static + + cocoapods-ios-16_1_beta: + runs-on: macos-14 + name: Test cocoapods-ios on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-cocoapods-ios + + cocoapods-watchos-16_1_beta: + runs-on: macos-14 + name: Test cocoapods-watchos on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-cocoapods-watchos + + cocoapods-tvos-16_1_beta: + runs-on: macos-14 + name: Test cocoapods-tvos on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-cocoapods-tvos + + cocoapods-catalyst-16_1_beta: + runs-on: macos-14 + name: Test cocoapods-catalyst on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-cocoapods-catalyst + + ios-swiftui-16_1_beta: + runs-on: macos-14 + name: Test ios-swiftui on Xcode 16.1_beta + env: + DEVELOPER_DIR: '/Applications/Xcode_16.1_beta.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-ios-swiftui diff --git a/.github/workflows/update-xcode-cloud-workflows.yml b/.github/workflows/update-xcode-cloud-workflows.yml deleted file mode 100644 index c4d5dd66d6..0000000000 --- a/.github/workflows/update-xcode-cloud-workflows.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Update XCode Cloud Workflows -on: workflow_dispatch -jobs: - main: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Get Token - id: token - run: | - token=$(ruby ./scripts/xcode_cloud_helper.rb --issuer-id ${{ secrets.APPLE_STORE_CONNECT_ISSUER_ID }} --key-id ${{ secrets.APPLE_STORE_CONNECT_KEY_ID }} --pk "${{ secrets.APPLE_STORE_CONNECT_API_KEY }}" get-token) - echo "TOKEN=$token" >> $GITHUB_OUTPUT - - name: Run ruby script - run: echo "both" | ./scripts/xcode_cloud_helper.rb -t ${{ steps.token.outputs.TOKEN }} synchronize-workflows - - diff --git a/RealmSwift/Tests/ObjectTests.swift b/RealmSwift/Tests/ObjectTests.swift index ec0f220550..6646a0047d 100644 --- a/RealmSwift/Tests/ObjectTests.swift +++ b/RealmSwift/Tests/ObjectTests.swift @@ -1641,7 +1641,7 @@ class ObjectTests: TestCase, @unchecked Sendable { let realm = try await Realm(actor: CustomGlobalActor.shared) let obj = realm.objects(SwiftObject.self).first! var value = 0 - for try await change in changesetPublisher(obj).values { + for try await change in changesetPublisher(obj).buffer(size: 10, prefetch: .byRequest, whenFull: .dropOldest).values { guard case let .change(object, props) = change else { return XCTFail("Expected .change, got \(change)") } @@ -1696,8 +1696,7 @@ class ObjectTests: TestCase, @unchecked Sendable { } @available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) - func testCancelTaskWhileWaitingForInitial() async throws { - return; // FIXME + func skip_testCancelTaskWhileWaitingForInitial() async throws { // This can't be tested deterministically as it's trying to hit specific // timing windows, so instead spawn a bunch of tasks and hope that at // least one is in each of the interesting states. Not handling all of @@ -1727,7 +1726,7 @@ class ObjectTests: TestCase, @unchecked Sendable { // Actor executors aren't fifo, so we can sometimes prevent the // async opens from ever completing by continuously spawning new // tasks - while waitingForRealm.value > 10 { + while waitingForRealm.value >= 10 { await Task.yield() } } diff --git a/build.sh b/build.sh index ba76d9743e..d4e32654d1 100755 --- a/build.sh +++ b/build.sh @@ -26,17 +26,13 @@ if [ -n "${CI}" ]; then CODESIGN_PARAMS=(CODE_SIGN_IDENTITY='' CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO) fi -if [ -n "${CI_XCODE_CLOUD}" ]; then - DERIVED_DATA="$CI_DERIVED_DATA_PATH" - ROOT_WORKSPACE="$CI_WORKSPACE" - BRANCH="$CI_BRANCH" -elif [ -n "${GITHUB_WORKSPACE}" ]; then - DERIVED_DATA="$GITHUB_WORKSPACE/build/DerivedData/Realm" +if [ -n "${GITHUB_WORKSPACE}" ]; then + DERIVED_DATA="$GITHUB_WORKSPACE/build/DerivedData" ROOT_WORKSPACE="$GITHUB_WORKSPACE" - BRANCH="$GITHUB_REF" + BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF}}" else ROOT_WORKSPACE="$(pwd)" - DERIVED_DATA="$ROOT_WORKSPACE/build/DerivedData/Realm" + DERIVED_DATA="$ROOT_WORKSPACE/build/DerivedData" BRANCH="$(git branch --show-current)" fi @@ -69,7 +65,7 @@ command: test-swiftpm: tests ObjC and Swift macOS frameworks via SwiftPM test-ios-swiftui: tests SwiftUI framework UI tests test-swiftuiserver-osx: tests Server Sync in SwiftUI - verify: verifies docs, cocoapods, swiftpm, xcframework, swiftuiserver-osx, swiftlint, spm-ios, objectserver-osx, watchos in both Debug and Release configurations + verify: verifies docs, cocoapods, swiftpm, xcframework, swiftuiserver-osx, swiftlint, spm-ios, sync, watchos in both Debug and Release configurations docs: builds docs in docs/output examples: builds all examples @@ -183,7 +179,7 @@ build_combined() { build_args=(-scheme "$product" -configuration "$config" build REALM_HIDE_SYMBOLS=YES) # Derive build paths - local build_products_path="$DERIVED_DATA/Build/Products" + local build_products_path="$DERIVED_DATA/Realm/Build/Products" local product_name="$product.framework" local os_path="$build_products_path/$config${config_suffix}/$product_name" local simulator_path="$build_products_path/$config-$simulator_suffix/$product_name" @@ -257,7 +253,7 @@ build_platform() { ;; esac - build_products_path="$DERIVED_DATA/Build/Products" + build_products_path="$DERIVED_DATA/Realm/Build/Products" build_path="$build_products_path/$config${config_suffix}" build_args=(-scheme "$product" -configuration "$config" build REALM_HIDE_SYMBOLS=YES) @@ -533,11 +529,11 @@ case "$COMMAND" in done # Assemble them into xcframeworks - rm -rf "$DERIVED_DATA/Build/Products"*.xcframework - find "$DERIVED_DATA/Build/Products" -name 'Realm.framework' \ + rm -rf "$DERIVED_DATA/Realm/Build/Products"*.xcframework + find "$DERIVED_DATA/Realm/Build/Products" -name 'Realm.framework' \ | sed 's/.*/-framework &/' \ | xargs xcodebuild -create-xcframework -allow-internal-distribution -output "build/$CONFIGURATION/Realm.xcframework" - find "$DERIVED_DATA/Build/Products" -name 'RealmSwift.framework' \ + find "$DERIVED_DATA/Realm/Build/Products" -name 'RealmSwift.framework' \ | sed 's/.*/-framework &/' \ | xargs xcodebuild -create-xcframework -allow-internal-distribution -output "build/$CONFIGURATION/RealmSwift.xcframework" @@ -648,7 +644,7 @@ case "$COMMAND" in exit 0 ;; - "test-objectserver-osx") + "test-sync") xctest 'Object Server Tests' -configuration "$CONFIGURATION" -sdk macosx -destination "platform=macOS,arch=$(uname -m)" exit 0 ;; @@ -669,7 +665,7 @@ case "$COMMAND" in ;; "test-ios-swiftui") - xctest 'SwiftUITestHost' -configuration "$CONFIGURATION" -sdk iphonesimulator -destination 'name=iPhone 11' + xctest 'SwiftUITestHost' -configuration "$CONFIGURATION" -sdk iphonesimulator -destination 'name=iPhone 14' exit 0 ;; @@ -683,6 +679,16 @@ case "$COMMAND" in exit 0 ;; + "test-visionos") + xctest Realm -configuration "$CONFIGURATION" -sdk xrsimulator -destination 'platform=visionOS Simulator,name=Apple Vision Pro' CODE_SIGN_IDENTITY='' + exit 0 + ;; + + "test-visionos-swift") + xctest RealmSwift -configuration "$CONFIGURATION" -sdk xrsimulator -destination 'platform=visionOS Simulator,name=Apple Vision Pro' CODE_SIGN_IDENTITY='' + exit 0 + ;; + "test-swiftuiserver-osx") xctest 'SwiftUISyncTestHost' -configuration "$CONFIGURATION" -sdk macosx -destination 'platform=macOS' exit 0 @@ -695,7 +701,7 @@ case "$COMMAND" in sh build.sh verify-cocoapods sh build.sh verify-docs sh build.sh verify-spm-ios - sh build.sh verify-objectserver-osx + sh build.sh verify-sync sh build.sh verify-swiftlint sh build.sh verify-swiftpm sh build.sh verify-watchos @@ -723,15 +729,15 @@ case "$COMMAND" in ;; "verify-cocoapods") - export REALM_TEST_BRANCH="$sha" + export REALM_TEST_BRANCH="$BRANCH" if [[ -d .git ]]; then # Verify the current branch, unless one was already specified in the sha environment variable. - if [[ -z $sha ]]; then + if [[ -z $BRANCH ]]; then export REALM_TEST_BRANCH=$(git rev-parse --abbrev-ref HEAD) fi if [[ $(git log -1 '@{push}..') != "" ]] || ! git diff-index --quiet HEAD; then - echo "WARNING: verify-cocoapods will test the latest revision of $sha found on GitHub." + echo "WARNING: verify-cocoapods will test the latest revision of $BRANCH found on GitHub." echo " Any unpushed local changes will not be tested." echo "" sleep 1 @@ -751,7 +757,7 @@ case "$COMMAND" in PLATFORM=$(echo "$COMMAND" | cut -d - -f 3) cd examples/installation - REALM_TEST_BRANCH="$sha" ./build.rb "$PLATFORM" cocoapods "$LINKAGE" + REALM_TEST_BRANCH="$BRANCH" ./build.rb "$PLATFORM" cocoapods "$LINKAGE" ;; "verify-docs") @@ -768,15 +774,15 @@ case "$COMMAND" in ;; "verify-spm") - export REALM_TEST_BRANCH="$sha" + export REALM_TEST_BRANCH="$BRANCH" if [[ -d .git ]]; then # Verify the current branch, unless one was already specified in the sha environment variable. - if [[ -z $sha ]]; then + if [[ -z $BRANCH ]]; then export REALM_TEST_BRANCH=$(git rev-parse --abbrev-ref HEAD) fi if [[ $(git log -1 '@{push}..') != "" ]] || ! git diff-index --quiet HEAD; then - echo "WARNING: verify-spm will test the latest revision of $sha found on GitHub." + echo "WARNING: verify-spm will test the latest revision of $BRANCH found on GitHub." echo " Any unpushed local changes will not be tested." echo "" sleep 1 @@ -797,12 +803,7 @@ case "$COMMAND" in PLATFORM=$(echo "$COMMAND" | cut -d - -f 3) cd examples/installation - REALM_TEST_BRANCH="$sha" ./build.rb "$PLATFORM" spm "$LINKAGE" - exit 0 - ;; - - "verify-objectserver-osx") - REALM_TEST_BRANCH="$sha" sh build.sh test-objectserver-osx + REALM_TEST_BRANCH="$BRANCH" ./build.rb "$PLATFORM" spm "$LINKAGE" exit 0 ;; @@ -837,9 +838,7 @@ case "$COMMAND" in sh build.sh examples-osx ( - DERIVED_EXAMPLE_DATA=${DERIVED_DATA:-examples/osx/objc/build/DerivedData/RealmExamples} - - cd $DERIVED_EXAMPLE_DATA/Build/Products/$CONFIGURATION + cd examples/osx/objc/build/DerivedData/RealmExamples/Build/Products/Release DYLD_FRAMEWORK_PATH=. ./JSONImport >/dev/null ) exit 0 diff --git a/ci_scripts/ci_post_clone.sh b/ci_scripts/ci_post_clone.sh deleted file mode 100755 index 6b867269a7..0000000000 --- a/ci_scripts/ci_post_clone.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/bash - -set -eo pipefail - -###################################### -# Dependency Installer -###################################### - -USE_BUNDLE_EXEC='' -install_dependencies() { - echo ">>> Installing dependencies for ${CI_WORKFLOW}" - - if [[ "$CI_WORKFLOW" == "docs"* ]]; then - install_ruby - elif [[ "$CI_WORKFLOW" == "swiftlint"* ]]; then - brew install swiftlint - elif [[ "$CI_WORKFLOW" == "cocoapods"* ]]; then - install_ruby - #elif [[ "$CI_WORKFLOW" == "sync"* ]]; then - # elif [[ "$CI_WORKFLOW" == "sync"* ]] || [[ "$CI_WORKFLOW" == "swiftpm"* ]]; then -# sh build.sh setup-baas -# sh build.sh download-core - elif [[ "$CI_WORKFLOW" = *"spm"* ]] || [[ "$CI_WORKFLOW" = "xcframework"* ]]; then - install_ruby - elif [[ "$CI_WORKFLOW" == *"carthage"* ]]; then - brew install carthage - else - sh build.sh download-core - fi -} - -install_ruby() { - echo ">>> Installing new Version of ruby" - brew install rbenv ruby-build - rbenv install - eval "$(rbenv init -)" - bundle install - USE_BUNDLE_EXEC=true -} - -env - -cd "$(dirname "$0")"/.. -install_dependencies - -# Xcode Cloud doesn't let us set the configuration to build, so set it by -# modifying the scheme files -target=$(echo "$CI_WORKFLOW" | cut -f1 -d_) -configuration="Release" -case "$target" in - *-debug) configuration="Debug" ;; - *-static) configuration="Static" ;; -esac - -find Realm.xcodeproj -name '*.xcscheme' \ - -exec sed -i '' "s/buildConfiguration = \"Debug\"/buildConfiguration = \"$configuration\"/" {} \; - -# If testing library evolution mode, patch the config to enable it -if [[ "$target" == *-evolution ]]; then - filename='Configuration/RealmSwift/RealmSwift.xcconfig' - sed -i '' "s/REALM_BUILD_LIBRARY_FOR_DISTRIBUTION = NO;/REALM_BUILD_LIBRARY_FOR_DISTRIBUTION = YES;/" "$filename" -fi - -# If testing encryption, patch the scheme to enable it -if [[ "$target" == *-encryption ]]; then - filename='Realm.xcodeproj/xcshareddata/xcschemes/Realm.xcscheme' - xmllint --shell "$filename" << EOF - cd /Scheme/LaunchAction/EnvironmentVariables/EnvironmentVariable[@key='REALM_ENCRYPT_ALL']/@isEnabled - set YES - save -EOF -fi - -# In release we are creating some workflows which build the framework for each platform, target and configuration, -# and we need to set the linker flags in the Configuration file. -if [[ "$target" == "release-package-build-"* ]]; then - filename="Configuration/Release.xcconfig" - sed -i '' "s/REALM_HIDE_SYMBOLS = NO;/REALM_HIDE_SYMBOLS = YES;/" "$filename" -fi - -# Xcode cloud currently doesn't have visionOS installed for Xcode 15.3 -if [[ "$CI_WORKFLOW" == release-package-build-vision*_15.3 ]]; then - xcodebuild -downloadAllPlatforms -fi - -# If we're building the dummy CI target then run the test. Other schemes are -# built via Xcode cloud's xcodebuild invocation. We can't do this via a build -# step on the CI target as that results in nested invocations of xcodebuild, -# which doesn't work. -if [[ "$CI_XCODE_SCHEME" == CI ]]; then - if [[ -n "$USE_BUNDLE_EXEC" ]]; then - bundle exec sh build.sh ci-pr - else - sh build.sh ci-pr - fi -fi diff --git a/ci_scripts/ci_pre_xcodebuild.sh b/ci_scripts/ci_pre_xcodebuild.sh deleted file mode 100755 index 7b670c9f42..0000000000 --- a/ci_scripts/ci_pre_xcodebuild.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -set -eo pipefail - -env - -if [[ "$CI_WORKFLOW" == "sync"* ]]; then - cd .. - sh build.sh setup-baas - sh build.sh download-core -fi diff --git a/dependencies.list b/dependencies.list index d8111aa27e..e54a052572 100755 --- a/dependencies.list +++ b/dependencies.list @@ -1,3 +1,3 @@ VERSION=10.53.1 REALM_CORE_VERSION=v14.12.1 -STITCH_VERSION=2f308db6f65333728a101d1fecbb792f9659a5ce +STITCH_VERSION=c794ec6e2b751ef0cb5ab35256b07f5fa0c74c3a diff --git a/examples/installation/SubRealm/SubRealm.podspec b/examples/installation/SubRealm/SubRealm.podspec index 8af535de9a..015ba2ef72 100644 --- a/examples/installation/SubRealm/SubRealm.podspec +++ b/examples/installation/SubRealm/SubRealm.podspec @@ -11,6 +11,7 @@ Pod::Spec.new do |s| s.osx.deployment_target = '10.15' s.watchos.deployment_target = '5.0' s.tvos.deployment_target = '12.0' + s.visionos.deployment_target = '1.0' s.source_files = "*.swift" s.dependency 'RealmSwift' end diff --git a/scripts/pr-ci-matrix.rb b/scripts/pr-ci-matrix.rb index f91e99ec5b..edc690e696 100755 --- a/scripts/pr-ci-matrix.rb +++ b/scripts/pr-ci-matrix.rb @@ -1,137 +1,100 @@ #!/usr/bin/env ruby -# Matrix of current targets and XCode versions, and is used to add/update/delete XCode cloud workflows. +XCODE_VERSIONS = %w(15.3 15.4 16_beta_6 16.1_beta) +DOC_VERSION = '15.4' -Destination = Struct.new(:build_platform, :test_destination) do |cls| - def cls.macOS - Destination.new('MACOS', { - 'deviceTypeName' => 'Mac', - 'deviceTypeIdentifier' => 'mac', - 'runtimeName' => 'Same As Selected macOS Version', - 'runtimeIdentifier' => 'builder', - 'kind' => 'MAC' - }) - end +all = ->(v) { true } +latest_only = ->(v) { v == XCODE_VERSIONS.last } +oldest_and_latest = ->(v) { v == XCODE_VERSIONS.first or v == XCODE_VERSIONS.last } - def cls.catalyst - Destination.new('MACOS', { - 'deviceTypeName' => 'Mac (Mac Catalyst)', - 'deviceTypeIdentifier' => 'mac_catalyst', - 'runtimeName' => 'Same As Selected macOS Version', - 'runtimeIdentifier' => 'builder', - 'kind' => 'MAC' - }) - end - - def cls.iOS - Destination.new('IOS', { - 'deviceTypeName' => 'iPhone 11', - 'deviceTypeIdentifier' => 'com.apple.CoreSimulator.SimDeviceType.iPhone-11', - 'runtimeName' => 'Latest from Selected Xcode (iOS 16.1)', - 'runtimeIdentifier' => 'default', - 'kind' => 'SIMULATOR' - }) - end - - def cls.tvOS - Destination.new('TVOS', { - 'deviceTypeName' => 'Recommended Apple TVs', - 'deviceTypeIdentifier' => 'recommended_apple_tvs', - 'runtimeName' => 'Latest from Selected Xcode (tvOS 16.4)', - 'runtimeIdentifier' => 'default', - 'kind' => 'SIMULATOR' - }) - end - - def cls.generic - Destination.new('MACOS', nil) - end +def minimum_version(major) + ->(v) { v.split('.').first.to_i >= major } end -Target = Struct.new(:name, :scheme, :filter, :destination) do - def action - action = { - name: self.name, - actionType: 'BUILD', - destination: nil, - buildDistributionAudience: nil, - scheme: self.scheme, - platform: self.destination.build_platform, - isRequiredToPass: true - } - - test_destination = self.destination.test_destination - if test_destination - action[:actionType] = 'TEST' - action[:destination] = 'ANY_MAC' - action[:testConfiguration] = { - kind: 'USE_SCHEME_SETTINGS', - testPlanName: '', - testDestinations: [test_destination] - } +targets = { + 'osx' => all, + 'osx-encryption' => latest_only, + + 'swiftpm' => oldest_and_latest, + 'swiftpm-debug' => all, + 'swiftpm-address' => latest_only, + 'swiftpm-thread' => latest_only, + + 'ios-static' => oldest_and_latest, + 'ios' => oldest_and_latest, + 'watchos' => oldest_and_latest, + 'tvos' => oldest_and_latest, + 'visionos' => oldest_and_latest, + + 'osx-swift' => all, + 'ios-swift' => oldest_and_latest, + 'tvos-swift' => oldest_and_latest, + + 'osx-swift-evolution' => latest_only, + 'ios-swift-evolution' => latest_only, + 'tvos-swift-evolution' => latest_only, + + 'catalyst' => oldest_and_latest, + 'catalyst-swift' => oldest_and_latest, + + 'xcframework' => latest_only, + + 'cocoapods-osx' => all, + 'cocoapods-ios-static' => latest_only, + 'cocoapods-ios' => latest_only, + 'cocoapods-watchos' => latest_only, + 'cocoapods-tvos' => latest_only, + 'cocoapods-catalyst' => latest_only, + 'ios-swiftui' => latest_only, +} + +output_file = """ +# This is a generated file produced by scripts/pr-ci-matrix.rb. +name: Pull request build and test +on: + pull_request: + paths-ignore: + - '**.md' + workflow_dispatch: + +jobs: + docs: + runs-on: macos-14 + name: Test docs + steps: + - uses: actions/checkout@v4 + - uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + - run: sudo xcode-select -switch /Applications/Xcode_#{DOC_VERSION}.app + - run: bundle exec sh build.sh verify-docs + swiftlint: + runs-on: macos-14 + name: Check swiftlint + steps: + - uses: actions/checkout@v4 + - run: sudo xcode-select -switch /Applications/Xcode_#{DOC_VERSION}.app + - run: brew install swiftlint + - run: sh build.sh verify-swiftlint +""" + +targets.each { |name, filter| + XCODE_VERSIONS.each { |version| + if not filter.call(version) + next end - - return action - end -end - -# Each test target has a name, a scheme, an xcode version filter, and a -# destination to run tests on. Targets which aren't testing a framework -# use the 'CI' target and always the a 'generic' destination. -# -# To avoid using excess CI resources we don't build the full matrix of -# combinations of targets and Xcode version. We generally test each build -# method (Xcode project, Swift package, and podspec) on every Xcode version for -# a single platform, and everything else is tested with the oldest and newest -# supported Xcode versions. Some things (e.g. swiftlint) only test the latest -# because they don't care about Xcode versions, while some others are latest-only -# because they're particularly slow to run. -module Workflows - XCODE_VERSIONS = %w(15.3 15.4 16\ beta\ 6 16.1\ beta) - - all = ->(v) { true } - latest_only = ->(v) { v == XCODE_VERSIONS.last } - oldest_and_latest = ->(v) { v == XCODE_VERSIONS.first or v == XCODE_VERSIONS.last } - - TARGETS = [ - Target.new('osx', 'Realm', all, Destination.macOS), - Target.new('osx-encryption', 'Realm', latest_only, Destination.macOS), - Target.new('osx-swift', 'RealmSwift', all, Destination.macOS), - Target.new('osx-swift-evolution', 'RealmSwift', latest_only, Destination.macOS), - - Target.new('ios', 'Realm', oldest_and_latest, Destination.iOS), - Target.new('ios-static', 'Realm', oldest_and_latest, Destination.iOS), - Target.new('ios-swift', 'RealmSwift', oldest_and_latest, Destination.iOS), - Target.new('ios-swift-evolution', 'RealmSwift', latest_only, Destination.iOS), - - Target.new('tvos', 'Realm', oldest_and_latest, Destination.tvOS), - Target.new('tvos-static', 'Realm', oldest_and_latest, Destination.tvOS), - Target.new('tvos-swift', 'RealmSwift', oldest_and_latest, Destination.tvOS), - Target.new('tvos-swift-evolution', 'RealmSwift', latest_only, Destination.tvOS), - - Target.new('catalyst', 'Realm', oldest_and_latest, Destination.catalyst), - Target.new('catalyst-swift', 'RealmSwift', oldest_and_latest, Destination.catalyst), - - Target.new('watchos', 'Realm', oldest_and_latest, Destination.generic), - Target.new('watchos-swift', 'RealmSwift', oldest_and_latest, Destination.generic), - - Target.new('swiftui', 'SwiftUITests', latest_only, Destination.iOS), - - Target.new('docs', 'CI', latest_only, Destination.generic), - Target.new('swiftlint', 'CI', latest_only, Destination.generic), - - Target.new('swiftpm', 'CI', oldest_and_latest, Destination.generic), - Target.new('swiftpm-debug', 'CI', all, Destination.generic), - Target.new('swiftpm-address', 'CI', latest_only, Destination.generic), - Target.new('swiftpm-thread', 'CI', latest_only, Destination.generic), - Target.new('spm-ios', 'CI', all, Destination.generic), - - Target.new('xcframework', 'CI', latest_only, Destination.generic), - - Target.new('cocoapods-osx', 'CI', all, Destination.generic), - Target.new('cocoapods-ios', 'CI', latest_only, Destination.generic), - Target.new('cocoapods-ios-static', 'CI', latest_only, Destination.generic), - Target.new('cocoapods-watchos', 'CI', latest_only, Destination.generic), - Target.new('cocoapods-tvos', 'CI', latest_only, Destination.generic), - Target.new('cocoapods-catalyst', 'CI', latest_only, Destination.generic), - ] + output_file << """ + #{name}-#{version.gsub(' ', '_').gsub('.', '_')}: + runs-on: macos-14 + name: Test #{name} on Xcode #{version} + env: + DEVELOPER_DIR: '/Applications/Xcode_#{version}.app/Contents/Developer' + steps: + - uses: actions/checkout@v4 + - run: sh -x build.sh verify-#{name} +""" + } +} + +File.open('.github/workflows/build-pr.yml', "w") do |file| + file.puts output_file end diff --git a/scripts/xcode_cloud_helper.rb b/scripts/xcode_cloud_helper.rb deleted file mode 100755 index 52726474f9..0000000000 --- a/scripts/xcode_cloud_helper.rb +++ /dev/null @@ -1,603 +0,0 @@ -#!/usr/bin/env ruby -require 'base64' -require 'json' -require 'jwt' -require 'net/http' -require 'net/https' -require 'optparse' -require 'pp' -require 'uri' -require_relative "pr-ci-matrix" -require_relative "release-matrix" - -include Workflows - -APP_STORE_URL = 'https://api.appstoreconnect.apple.com/v1' -HTTP = Net::HTTP.new('api.appstoreconnect.apple.com', 443) -HTTP.use_ssl = true -HTTP.max_retries = 2 - -def sh(*args) - puts "executing: #{args.join(' ')}" if false - system(*args, false ? {} : {:out => '/dev/null'}) || exit(1) -end - -def request(req) - req['Authorization'] = "Bearer #{JWT_TOKEN}" - # puts req.path - counter = 0 - while true do - sleep 5 - counter +=1 - response = HTTP.request(req) - break if (response.code =~ /20./ ) == 0 - break if counter == 2 - end - - raise "Error: #{response.code} #{response.body}" unless response.code =~ /20./ - response -end - -def get(path) - req = Net::HTTP::Get.new("/v1/#{path}") - req['Accept'] = 'application/json' - response = request req - # puts response.body - JSON.parse(response.body) -end - -def post(path, body) - req = Net::HTTP::Post.new("/v1/#{path}") - req['Content-Type'] = 'application/json' - req.body = body.to_json - response = request req - JSON.parse(response.body) -end - -def get_jwt_bearer_from_file(issuer_id, key_id, pk_path) - private_key = File.read(pk_path) - get_jwt_bearer(issuer_id, key_id, private_key) -end - -def get_jwt_bearer(issuer_id, key_id, pk) - private_key = OpenSSL::PKey.read(pk) - info = { - iss: issuer_id, - exp: Time.now.to_i + 20 * 60, - aud: 'appstoreconnect-v1' - } - header_fields = { kid: key_id } - JWT.encode(info, private_key, 'ES256', header_fields) -end - -def get_workflows - product_id = get_realm_product_id - get("/ciProducts/#{product_id}/workflows?limit=200")['data'] -end - -def get_products - get('ciProducts')['data'].map do |product| - { - id: product['id'], - product: product['attributes']['name'], - type: product['attributes']['productType'] - } - end -end - -def get_repositories - response = get('scmRepositories')['data'].map do |repo| - { - id: repo['id'], - name: repo['attributes']['repositoryName'], - url: repo['attributes']['httpCloneUrl'] - } - end -end - -def get_macos_versions - get('ciMacOsVersions')['data'].map do |macos| - { - id: macos['id'], - name: macos['attributes']['name'], - version: macos['attributes']['version'] - } - end -end - -def get_xcode_versions - data = get('ciXcodeVersions')['data'] - Hash[data.collect { |xcode| - [xcode['attributes']['name'], xcode['id']] - }] -end - -def get_build_actions(build_run) - get("/ciBuildRuns/#{build_run}/actions")['data'].map do |build_run| - { - id: build_run['id'], - } - end -end - -def get_artifacts(build_action) - get("/ciBuildActions/#{build_action}/artifacts")['data'].map do |artifact| - { - id: artifact['id'], - } - end -end - -def get_git_references - repository_id = get_realm_repository_id - get("/scmRepositories/#{repository_id}/gitReferences?limit=200") -end - -def get_workflow_info(id) - get("ciWorkflows/#{id}") -end - -def get_build_info(id) - get("/ciBuildRuns/#{id}") -end - -def get_artifact_info(id) - get("/ciArtifacts/#{id}") -end - -def create_workflow(target, xcode_version, pull_request) - result = post('ciWorkflows', create_workflow_request(target, xcode_version, pull_request)) - id = result["data"]["id"] - return id -end - -def create_workflow_request(target, xcode_version, pull_request) - xcode_version_id = get_xcode_id(xcode_version) - data = { - data: { - type: 'ciWorkflows', - attributes: { - name: "#{target.name}_#{xcode_version}", - description: 'Create by Github Action Update XCode Cloud Workflows', - isLockedForEditing: false, - containerFilePath: 'Realm.xcodeproj', - isEnabled: true, - clean: false, - actions: [target.action] - }, - relationships: { - xcodeVersion: { - data: { - type: 'ciXcodeVersions', - id: xcode_version_id - } - }, - macOsVersion: { - data: { - type: 'ciMacOsVersions', - id: get_macos_latest_release(xcode_version_id) - } - }, - product: { - data: { - type: 'ciProducts', - id: get_realm_product_id - } - }, - repository: { - data: { - type: 'scmRepositories', - id: get_realm_repository_id - } - } - } - } - } - - if pull_request - data[:data][:attributes][:pullRequestStartCondition] = { - source: { - isAllMatch: true, - }, - destination: { - isAllMatch: true, - }, - autoCancel: true - } - else - data[:data][:attributes][:manualBranchStartCondition] = { - source: { - isAllMatch: true, - }, - destination: { - isAllMatch: true, - }, - autoCancel: true - } - end - - data -end - -def enable_workflow(id) - req = Net::HTTP::Patch.new("/v1/ciWorkflows/#{id}") - req['Content-type'] = 'application/json' - req.body = { - data: { - type: 'ciWorkflows', - attributes: { - isEnabled: true - }, - id: id - } - }.to_json - response = request req - puts response.body - result = JSON.parse(response.body) - id = result['data']['id'] - puts "Workflow updated #{id}" - return id -end - -def delete_workflow(id) - req = Net::HTTP::Delete.new("/v1/ciWorkflows/#{id}") - req['Content-type'] = 'application/json' - response = request req -end - -def start_build(id, branch) - branch_id = find_git_reference_for_branch(branch) - result = post('ciBuildRuns', { - data: { - type: 'ciBuildRuns', - attributes: {}, - relationships: { - workflow: { - data: { - type: 'ciWorkflows', - id: id - } - }, - sourceBranchOrTag: { - data: { - type: 'scmGitReferences', - id: branch_id - } - } - } - } - }) - id = result['data']['id'] - return id -end - -def get_macos_version_for_xcode_version(version) - result = get("ciXcodeVersions/#{version}/macOsVersions") - latest = result['data'].find { |version| version['attributes']['name'] == 'Latest Release' } - latest['id'] -end - -def synchronize_workflows() - desired_workflows = Workflows::TARGETS.flat_map { |target| - Workflows::XCODE_VERSIONS.filter_map { |version| - if target.filter.call(version) - {target: target, version: version} - end - } - } - current_workflows = get_workflows.filter_map { |workflow| - name = workflow['attributes']['name'] - # don't touch release pipeline jobs - next if name.start_with? 'release-' - pieces = name.partition('_') - {name: pieces.first, version: pieces.last, id: workflow['id']} - } - - workflows_to_remove = current_workflows.reject { |current| - desired_workflows.find { |desired| - desired[:target].name == current[:name] && desired[:version] == current[:version] - } - } - workflows_to_create = desired_workflows.reject { |desired| - current_workflows.find { |current| - desired[:target].name == current[:name] && desired[:version] == current[:version] - } - } - - puts 'Workflows to remove:' - workflows_to_remove.each { |w| - puts "- #{w[:name]}: #{w[:version]}" - } - puts '' - puts 'Workflows to create:' - workflows_to_create.each { |w| - puts "- #{w[:target].name}: #{w[:version]}" - } - puts '' - print 'Do you wish to continue [create/delete/both/quit]? ' - - case STDIN.gets.chomp.downcase - when 'create' - workflows_to_remove = [] - when 'delete' - workflows_to_create = [] - when 'both' - when 'quit' - puts 'Exiting without making any changes' - exit 0 - else - puts 'Unrecoginized command' - exit 1 - end - - workflows_to_create.each { |w| - id = create_workflow(w[:target], w[:version], true) - puts "#{w[:target]}: https://appstoreconnect.apple.com/teams/69a6de86-7f37-47e3-e053-5b8c7c11a4d1/frameworks/#{get_realm_product_id}/workflows/#{id}" - } - workflows_to_remove.each { |w| - delete_workflow(w[:id]) - puts "Workflow deleted #{w[:name]}" - } -end - -def get_build_status(build_run) - build_state = get_build_info(build_run) - status = build_state["data"]["attributes"]["executionProgress"] - return status -end - -def get_build_result(build_run) - build_state = get_build_info(build_run) - completion_status = build_state["data"]["attributes"]["completionStatus"] - return completion_status -end - -def get_logs_for_build(build_run) - actions = get_build_actions(build_run) - artifacts = get_artifacts(actions[0][:id]) # we are only running one action, so we use the first one in the list - artifact_url = '' - artifacts.each { |artifact| - artifact_info = get_artifact_info(artifact[:id]) - if artifact_info["data"]["attributes"]["fileName"].include? 'Logs' - artifact_url = artifact_info["data"]["attributes"]["downloadUrl"] - end - } - print_logs(artifact_url) -end - -def print_logs(url) - sh 'curl', '--output', 'logs.zip', "#{url}" - sh 'unzip', '-o', 'logs.zip' - log_files = Dir["RealmSwift*/*.log"] - log_files.each { |log_file| - text = File.readlines("#{log_file}").map do |line| - puts line - end - } -end - -def find_git_reference_for_branch(branch) - next_page = '' - references = get_git_references - branch_reference = references["data"].find { |reference| - reference["attributes"]["kind"] == "BRANCH" && reference["attributes"]["name"] == branch - } - while branch_reference == nil || next_page == nil - next_page = references["links"]["next"] - next_page.slice!(APP_STORE_URL) - references = get(next_page) - branch_reference = references["data"].find { |reference| reference["attributes"]["kind"] == "BRANCH" && reference["attributes"]["name"] == branch } - end - return branch_reference["id"] -end - -def download_artifact_for_build(build_id_run) - actions = get_build_actions(build_id_run) - artifacts = get_artifacts(actions[0][:id]) # One actions per workflow - artifact_url = '' - artifacts.each { |artifact| - artifact_info = get_artifact_info(artifact[:id]) - if artifact_info["data"]["attributes"]["fileName"].include? 'Products' - artifact_url = artifact_info["data"]["attributes"]["downloadUrl"] - end - } - - sh 'curl', '--output', "xcode-cloud-build-#{build_id_run}.zip", "#{artifact_url}" -end - -def clean_up_release_workflows() - workflows_to_remove = get_workflows.filter_map { |workflow| - if workflow['attributes']['name'].start_with?('release-package-build') - {name: workflow['attributes']['name'], id: workflow['id']} - end - } - workflows_to_remove.each { |w| - delete_workflow(w[:id]) - puts "Workflow deleted #{w[:name]}" - } -end - -$xcode_ids = nil -def get_xcode_id(version) - $xcode_ids ||= get_xcode_versions - id = $xcode_ids["Xcode #{version}"] - if not id - puts "Nonexistent Xcode version #{version}" - puts "Valid versions are:" - $xcode_ids.keys.each { |v| puts "- #{v}"} - exit 1 - end - id -end - -$mac_dict = Hash.new { |h, k| h[k] = get_macos_version_for_xcode_version(k) } -def get_macos_latest_release(xcodeVersionId) - return $mac_dict[xcodeVersionId] -end - -$product_id = nil -def get_realm_product_id - $product_id ||= get_products.find { |p| p[:product] == 'RealmSwift' }[:id] -end - -$repository_id = nil -def get_realm_repository_id - $repository_id ||= get_repositories.find { |repo| repo[:name] == 'realm-swift' }[:id] -end - -$workflows_list = nil -def get_workflow_id_for_name(name) - $workflows_list ||= get_workflows - $workflows_list.find { |w| w['attributes']['name'].split('_')[0] == name }['id'] -end - -Options = Struct.new(:token, :team_id, :issuer_id, :key_id, :pk_path, :pk) -options = Options.new() -$parser = OptionParser.new do |opts| - opts.banner = "Usage: ruby #{__FILE__} [options] command" - opts.separator <<~END - - All commands require either --token or all three of --issuer-id, --key-id, - and --pk-path or --pk to automatically create a token. - - Commands: - list-workflows - Returns a list of current workflows for the RealmSwift product. - list-products - Returns a list of products associated to the Apple Connect Store account. - list-repositories - Returns a list of repositories integrated with XCode Cloud. - list-mac-versions - Returns a list of available mac versions. - list-xcode-versions - Returns a list of available xcode version. - info-workflow workflow_id - Returns the info for the corresponding workflow. - synchronize-workflows - Delete old workflows and/or create new ones. - build-workflow workflow-id - Run a build for the corresponding workflow. - create-workflow platform xcode_version target configuration - Creates a workflow to create platform framework for an specific configuration, target and xcode version. - delete-workflow workflow-id - Deletes the workflow. - wait-build build_id - Check status of a current build and waits, returns when completed or fails. - download-artifact build_id - Download a build artifact for any given build run with a build action. - clean-up-release-workflows - Cleans all workflows created for a release (starts with release-package-build). - get-build-status - Get build current status. - get-build-result - Get build run completion status (Complete, Error). - print-build-logs - Print build logs. - get-token - Get Apple Connect Store API Token for local use. - - Options: - END - - opts.on("-h", "--help", "Display this help") do - puts opts - exit 0 - end - opts.on('-t TOKEN', '--token TOKEN', 'Apple Connect API token') do |token| - options[:token] = token - end - opts.on('--issuer-id ID', 'Apple Connect API Issuer ID.') do |id| - options[:issuer_id] = id - end - opts.on('--key-id ID', 'Apple Connect API Key ID.') do |id| - options[:key_id] = id - end - opts.on('--pk-path PATH', 'Apple Connect API path to private key file.') do |path| - options[:pk_path] = path - end - opts.on('--pk PK', 'Apple Connect API private key.') do |pk| - options[:pk] = pk - end -end -$parser.parse! - -def usage() - puts $parser - exit 1 -end - -if options[:issuer_id] and options[:key_id] and options[:pk_path] - JWT_TOKEN = get_jwt_bearer_from_file(options[:issuer_id], options[:key_id], options[:pk_path]) -elsif options[:issuer_id] and options[:key_id] and options[:pk] - JWT_TOKEN = get_jwt_bearer(options[:issuer_id], options[:key_id], options[:pk]) -elsif options[:token] - JWT_TOKEN = options[:token] -else - usage -end - -COMMAND = ARGV.shift -usage unless COMMAND - -case COMMAND -when 'list-workflows' - pp get_workflows -when 'list-products' - pp get_products -when 'list-repositories' - pp get_repositories -when 'list-mac-versions' - pp get_macos_versions -when 'list-xcode-versions' - pp get_xcode_versions -when 'info-workflow' - workflow_id = ARGV.shift - usage unless workflow_id - pp get_workflow_info(workflow_id) -when 'enable-workflow' - workflow_id = ARGV.shift - usage unless workflow_id - enable_workflow(workflow_id) -when 'synchronize-workflows' - synchronize_workflows() -when 'build-workflow' - workflow_id = ARGV.shift - branch = ARGV.shift - usage unless workflow_id - id = start_build(workflow_id, branch) - puts id -when 'create-workflow' - prefix = ARGV.shift - platform = ARGV.shift - xcode_version = ARGV.shift - target = ARGV.shift - configuration = ARGV.shift - usage unless platform and xcode_version and target and configuration - release_target = ReleaseTarget.new("#{prefix}-#{platform}-#{target}-#{configuration}", target, platform) - id = create_workflow(release_target, xcode_version, false) - puts id -when 'delete-workflow' - workflow_id = ARGV.shift - delete_workflow(workflow_id) -when 'download-artifact' - build_id = ARGV.shift - usage unless build_id - download_artifact_for_build(build_id) -when 'clean-up-release-workflows' - clean_up_release_workflows() -when 'get-build-status' - build_id = ARGV.shift - status = get_build_status(build_id) - puts status -when 'get-build-result' - build_id = ARGV.shift - usage unless build_id - completion_status = get_build_result(build_id) - puts completion_status -when 'print-build-logs' - build_id = ARGV.shift - usage unless build_id - get_logs_for_build(build_id) -when 'get-token' - puts JWT_TOKEN -end