diff --git a/.github/workflows/master-push.yml b/.github/workflows/master-push.yml new file mode 100644 index 0000000000..c6c4b90345 --- /dev/null +++ b/.github/workflows/master-push.yml @@ -0,0 +1,416 @@ +name: Prepare Release +on: + push: + branches: + - "master" +env: + XCODE_VERSION: "['14.2', '14.3.1', '15.1', '15.2']" + PLATFORM: "['ios', 'osx', 'watchos', 'tvos', 'catalyst', 'visionos']" + BUILD_PLATFORM: "['ios', 'iossimulator', 'osx', 'watchos', 'watchossimulator', 'tvos', 'tvossimulator', 'catalyst', 'visionos', 'visionossimulator']" + DOC_VERSION: '15.1' + JAZZY_VERSION: '0.14.4' + RELEASE_VERSION: '15.2' + TEST_VERSION: '15.1' +jobs: + cleanup: # Clean-up XCode cloud workflows, before running the pipeline + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Get Token + id: token + run: | + token=$(echo $(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: Clean up release-packages xcode cloud workflows + run: | + ruby ./scripts/xcode_cloud_helper.rb clean-up-release-workflows -t ${{ steps.token.outputs.TOKEN }} + prepare: + runs-on: ubuntu-latest + name: Prepare outputs + needs: [cleanup] + outputs: + XCODE_VERSIONS_MATRIX: ${{ env.XCODE_VERSION }} + BUILD_PLATFORM_MATRIX: ${{ env.BUILD_PLATFORM }} + PLATFORM_MATRIX: ${{ env.PLATFORM }} + VERSION: ${{ steps.get-version.outputs.VERSION }} + steps: + - name: Compute outputs + run: | + echo "XCODE_VERSIONS_MATRIX=${{ env.XCODE_VERSION }}" >> $GITHUB_OUTPUT + echo "PLATFORM_MATRIX=${{ env.PLATFORM }}" >> $GITHUB_OUTPUT + echo "BUILD_PLATFORM_MATRIX=${{ env.BUILD_PLATFORM }}" >> $GITHUB_OUTPUT + - name: Checkout + uses: actions/checkout@v4 + - name: Read SDK version + id: get-version + run: | + version="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${GITHUB_WORKSPACE}/dependencies.list")" + echo "VERSION=$version" >> $GITHUB_OUTPUT + build-docs: + runs-on: macos-13 + name: Package docs + needs: prepare + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Jazzy + run: gem install jazzy -v ${{ env.JAZZY_VERSION }} # This is only used once, no need to cache this + - name: Select Xcode Version + run: sudo xcode-select -switch /Applications/Xcode_${{ env.DOC_VERSION }}.app + - name: Prepare docs for packaging + run: sh -x build.sh release-package-docs + - name: Upload docs to artifacts library + uses: actions/upload-artifact@v4 + with: + name: realm-docs + path: docs/realm-docs.zip + build-examples: + runs-on: macos-13 + name: Package examples + needs: prepare + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Prepare examples for packaging + run: sh -x build.sh release-package-examples + - name: Upload examples to artifacts library + uses: actions/upload-artifact@v4 + with: + path: realm-examples.zip + name: realm-examples + build-product: # Creates framework for each platform, xcode version, target and configuration + runs-on: macos-13 + name: Package framework + needs: prepare + strategy: + max-parallel: 20 # Blocks of 20, therefore if any of the build fails, we don't get a lot of XCode Clouds builds hanging, which are expensive. + matrix: + platform: ${{ fromJSON(needs.prepare.outputs.BUILD_PLATFORM_MATRIX) }} + target: [RealmSwift, Realm] + xcode-version: ${{ fromJSON(needs.prepare.outputs.XCODE_VERSIONS_MATRIX) }} + configuration: [release, static] + exclude: + - platform: osx + configuration: static + - platform: tvos + configuration: static + - platform: watchos + configuration: static + - platform: visionos + configuration: static + - platform: catalyst + configuration: static + - platform: tvossimulator + configuration: static + - platform: watchossimulator + configuration: static + - platform: visionossimulator + configuration: static + - target: RealmSwift + configuration: static + - platform: visionos + xcode-version: 14.2 + - platform: visionossimulator + xcode-version: 14.2 + - platform: visionos + xcode-version: 14.3.1 + - platform: visionossimulator + xcode-version: 14.3.1 + - platform: visionos + xcode-version: 15.1 + - platform: visionossimulator + xcode-version: 15.1 + steps: + - name: Checkout + 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: Creates on demand XCode Cloud's workflows with a build action + id: create-workflow + uses: nick-fields/retry@v2 + with: + timeout_minutes: 10 + max_attempts: 3 + command: | + workflow_id=$(ruby ./scripts/xcode_cloud_helper.rb create-workflow release-package-build ${{ matrix.platform }} ${{ matrix.xcode-version }} ${{ matrix.target }} ${{ matrix.configuration }} -t ${{ steps.token.outputs.TOKEN }}) + echo "WORKFLOW_ID=$workflow_id" >> $GITHUB_OUTPUT + - name: Runs the XCode Cloud workflow created by the previous step + id: build-run + uses: nick-fields/retry@v2 + with: + timeout_minutes: 10 + max_attempts: 3 + command: | + build_run_id=$(ruby ./scripts/xcode_cloud_helper.rb build-workflow ${{ steps.create-workflow.outputs.WORKFLOW_ID }} ${{ github.ref_name }} -t ${{ steps.token.outputs.TOKEN }}) + echo "BUILD_RUN_ID=$build_run_id" >> $GITHUB_OUTPUT + - name: Check build status and wait for it to finish + uses: nick-fields/retry@v2 + with: + timeout_minutes: 10 + max_attempts: 3 + command: | + while [ "$status" != 'COMPLETE' ] + do + 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) + status=$(ruby ./scripts/xcode_cloud_helper.rb get-build-status ${{ steps.build-run.outputs.BUILD_RUN_ID }} -t $token) + echo "Status $status" + sleep 20 + done + completion_status=$(ruby ./scripts/xcode_cloud_helper.rb get-build-result ${{ steps.build-run.outputs.BUILD_RUN_ID }} -t $token) + echo "Completion Status $completion_status" + if [ "$completion_status" != 'SUCCEEDED' ]; then + echo "XCode Cloud build failed" + ruby ./scripts/xcode_cloud_helper.rb print-build-logs ${{ steps.build-run.outputs.BUILD_RUN_ID }} -t $token + exit 1 + fi + ruby ./scripts/xcode_cloud_helper.rb print-build-logs ${{ steps.build-run.outputs.BUILD_RUN_ID }} -t $token + - name: Download framework package from XCode Cloud artifacts + uses: nick-fields/retry@v2 + with: + timeout_minutes: 10 + max_attempts: 3 + command: | + 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) + ruby ./scripts/xcode_cloud_helper.rb download-artifact ${{ steps.build-run.outputs.BUILD_RUN_ID }} -t $token + - name: Prepare product folder + uses: nick-fields/retry@v2 + with: + timeout_minutes: 10 + max_attempts: 3 + command: | + sh -x build.sh release-package-product + - name: Upload framework + uses: actions/upload-artifact@v4 + with: + name: product-${{ matrix.platform }}-${{ matrix.xcode-version }}-${{ matrix.target }}-${{ matrix.configuration }} + path: product-package + # build-product: # Creates framework for each platform, xcode version, target and configuration + # runs-on: macos-13 + # name: Package framework + # needs: prepare + # strategy: + # matrix: + # platform: ${{ fromJSON(needs.prepare.outputs.BUILD_PLATFORM_MATRIX) }} + # target: [RealmSwift, Realm] + # xcode-version: ${{ fromJSON(needs.prepare.outputs.XCODE_VERSIONS_MATRIX) }} + # configuration: [release, static] + # exclude: + # - platform: osx + # configuration: static + # - platform: tvos + # configuration: static + # - platform: watchos + # configuration: static + # - platform: visionos + # configuration: static + # - platform: catalyst + # configuration: static + # - platform: tvossimulator + # configuration: static + # - platform: watchossimulator + # configuration: static + # - platform: visionossimulator + # configuration: static + # - target: RealmSwift + # configuration: static + # - platform: visionos + # xcode-version: 14.2 + # - platform: visionossimulator + # xcode-version: 14.2 + # - platform: visionos + # xcode-version: 14.3.1 + # - platform: visionossimulator + # xcode-version: 14.3.1 + # - platform: visionos + # xcode-version: 15.1 + # - platform: visionossimulator + # xcode-version: 15.1 + # steps: + # - name: Checkout + # uses: actions/checkout@v4 + # - name: Select Xcode Version + # run: sudo xcode-select -switch /Applications/Xcode_${{ matrix.xcode-version }}.app + # - name: Download visionOS, only for 15.2 + # if: ${{ matrix.platform == 'visionos' || matrix.platform == 'visionossimulator' }} + # run: xcodebuild -downloadPlatform visionOS + # - name: Create framework + # run: sh -x build.sh release-package-build_${{ matrix.platform }}-${{ matrix.target }}-${{ matrix.configuration }} + # - name: Prepare product folder + # run: sh -x build.sh release-package-product + # - name: Upload build product + # uses: actions/upload-artifact@v4 + # with: + # name: product-${{ matrix.platform }}-${{ matrix.xcode-version }}-${{ matrix.target }}-${{ matrix.configuration }} + # path: product-package + package-xcframework-platform: # Creates xcframework for each platform and xcode version + runs-on: macos-13 + name: Package xcframework for platform + needs: [build-product, prepare] + strategy: + matrix: + platform: ${{ fromJSON(needs.prepare.outputs.PLATFORM_MATRIX) }} + xcode-version: ${{ fromJSON(needs.prepare.outputs.XCODE_VERSIONS_MATRIX) }} + exclude: + - platform: visionos + xcode-version: 14.2 + - platform: visionos + xcode-version: 14.3.1 + - platform: visionos + xcode-version: 15.1 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Select Xcode Version + run: sudo xcode-select -switch /Applications/Xcode_${{ matrix.xcode-version }}.app + - name: Restore frameworks + uses: actions/download-artifact@v4 + with: + pattern: product-${{ matrix.platform }}*-${{ matrix.xcode-version }}-* + - name: Create xcframework + run: sh -x build.sh release-create-xcframework_${{ matrix.xcode-version }} ${{ matrix.platform }} + - name: Upload xcframework + uses: actions/upload-artifact@v4 + with: + name: realm-${{ matrix.platform }}-${{ matrix.xcode-version }} + path: realm-${{ matrix.platform }}-${{ matrix.xcode-version }}.zip + package-release: # Creates xcframework for each platform and xcode version + runs-on: macos-13 + name: Package release file + needs: [package-xcframework-platform, prepare] + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Select Xcode Version + run: sudo xcode-select -switch /Applications/Xcode_${{ env.RELEASE_VERSION }}.app + - name: Restore packages xcframeworks + uses: actions/download-artifact@v4 + with: + pattern: realm-* + - name: Create release + run: sh -x build.sh release-package + - name: Upload release artifactss + uses: actions/upload-artifact@v4 + with: + name: release-package + path: pkg/*.zip + - name: Upload release for testing + uses: actions/upload-artifact@v4 + with: + name: realm-swift-${{ needs.prepare.outputs.VERSION }} + path: pkg/realm-swift-${{ needs.prepare.outputs.VERSION }}.zip + test-package-examples: + runs-on: macos-13 + name: Test examples + needs: [package-release, prepare] + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Select Xcode Version + run: sudo xcode-select -switch /Applications/Xcode_${{ env.TEST_VERSION }}.app + - name: Restore release + uses: actions/download-artifact@v4 + with: + name: realm-swift-${{ needs.prepare.outputs.VERSION }} + - name: Test examples + run: sh -x build.sh release-test-examples + test-ios-static: + runs-on: macos-13 + name: Run tests on iOS with configuration Static + needs: package-release + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Select Xcode Version + run: sudo xcode-select -switch /Applications/Xcode_${{ env.TEST_VERSION }}.app + - name: Test ios static + run: sh -x build.sh test-ios-static + test-osx-static: + runs-on: macos-13 + name: Run tests on macOS + needs: package-release + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Select Xcode Version + run: sudo xcode-select -switch /Applications/Xcode_${{ env.TEST_VERSION }}.app + - name: Test osx static + run: | + export REALM_DISABLE_METADATA_ENCRYPTION=1 + sh -x build.sh test-osx + test-installation: + runs-on: macos-13 + name: Run installation test + needs: [package-release, prepare] + strategy: + matrix: + platform: ${{ fromJSON(needs.prepare.outputs.PLATFORM_MATRIX) }} + installation: [cocoapods, spm, carthage, xcframework] + linkage: [dynamic, static] + exclude: + - platform: visionos + - platform: catalyst + installation: carthage + - installation: carthage + linkage: static + - installation: xcframework + linkage: static + include: + - platform: ios + installation: xcframework + linkage: static + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Select Xcode Version + run: sudo xcode-select -switch /Applications/Xcode_${{ env.TEST_VERSION }}.app + - name: Install FileUtils + run: gem install fileutils + - name: Restore release + uses: actions/download-artifact@v4 + if: ${{ matrix.installation == 'xcframework' }} + with: + name: realm-${{ matrix.platform }}-${{ env.TEST_VERSION }} + - name: Unzip package release + if: ${{ matrix.installation == 'xcframework' }} + run: | + mkdir -p build + unzip realm-${{ matrix.platform }}-${{ env.TEST_VERSION }}.zip -d build + - name: Run installation test + run: | + echo "REALM_TEST_BRANCH=${{ github.ref_name }}" >> $GITHUB_OUTPUT + cd examples/installation + ./build.rb ${{ matrix.platform }} ${{ matrix.installation }} ${{ matrix.linkage }} + test-installation-xcframework: + runs-on: macos-13 + name: Run installation test for xcframework + needs: [package-release, prepare] + env: + PLATFORM: 'osx' + strategy: + matrix: + xcode-version: ${{ fromJSON(needs.prepare.outputs.XCODE_VERSIONS_MATRIX) }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install FileUtils + run: gem install fileutils + - name: Select Xcode Version + run: sudo xcode-select -switch /Applications/Xcode_${{ matrix.xcode-version }}.app + - name: Restore release + uses: actions/download-artifact@v4 + with: + name: realm-${{ env.PLATFORM }}-${{ matrix.xcode-version }} + - name: Unzip package release + run: | + mkdir -p build + unzip realm-${{ env.PLATFORM }}-${{ matrix.xcode-version }}.zip -d build + - name: Run installation test + run: | + echo "REALM_TEST_BRANCH=${{ github.ref_name }}" >> $GITHUB_OUTPUT + cd examples/installation + ./build.rb osx xcframework dynamic + \ No newline at end of file diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml new file mode 100644 index 0000000000..ab64d9313d --- /dev/null +++ b/.github/workflows/publish-release.yml @@ -0,0 +1,178 @@ +name: Publish release +on: workflow_dispatch +env: + XCODE_VERSION: "['14.2', '14.3.1', '15.1', '15.2']" + TEST_XCODE_VERSION: '15.1' +jobs: + prepare: + runs-on: ubuntu-latest + name: Prepare outputs + outputs: + XCODE_VERSIONS_MATRIX: ${{ env.XCODE_VERSION }} + VERSION: ${{ steps.get-version.outputs.VERSION }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Compute outputs + run: | + echo "XCODE_VERSIONS_MATRIX=${{ env.XCODE_VERSION }}" >> $GITHUB_OUTPUT + - name: Read SDK version + id: get-version + run: | + version="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${GITHUB_WORKSPACE}/dependencies.list")" + echo "VERSION=$version" >> $GITHUB_OUTPUT + tag-release: + runs-on: ubuntu-latest + name: Tag Release + needs: prepare + steps: + - name: Checkout + uses: actions/checkout@v4 + - uses: rickstaa/action-create-tag@v1 + id: "tag_create" + with: + tag: "v${{ needs.prepare.outputs.VERSION }}" + tag_exists_error: false + message: "" + publish-docs: + runs-on: macos-latest + name: Publish docs to S3 Bucket + needs: tag-release + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Gem install + run: | + gem install octokit + gem install pathname + gem install fileutils + - name: Export GITHUB_TOKEN to workspace + run: echo "GITHUB_ACCESS_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV + - name: Publish docs + run: | + sh -x build.sh publish-docs ${{ github.sha }} + create-release: + runs-on: macos-latest + name: Create github release + needs: [tag-release, prepare] + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Gem install + run: | + gem install octokit + gem install pathname + gem install fileutils + - name: Export GITHUB_TOKEN to workspace + run: echo "GITHUB_ACCESS_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV + - name: Create Github release + run: ./build.sh publish-github ${{ github.sha }} + publish-cocoapods: + runs-on: macos-latest + name: Publish Cocoapods specs + needs: [tag-release, prepare] + env: + COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install ruby + uses: ruby/setup-ruby@ec02537da5712d66d4d50a0f33b7eb52773b5ed1 + with: + ruby-version: '3.1.2' + bundler-cache: true + - name: Publish + run: ./build.sh publish-cocoapods v${{ needs.prepare.outputs.VERSION }} + update-checker: + runs-on: macos-latest + name: Update to latest version update checker file + needs: tag-release + env: + AWS_ACCESS_KEY_ID: ${{ secrets.UPDATE_CHECKER_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.UPDATE_CHECKER_SECRET_KEY }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install ruby + uses: ruby/setup-ruby@ec02537da5712d66d4d50a0f33b7eb52773b5ed1 + with: + ruby-version: '3.1.2' + bundler-cache: true + - name: Create Github release + run: ./build.sh publish-update-checker + test-installation: + runs-on: macos-13 + name: Run installation test for ${{ matrix.platform }}, ${{ matrix.installation }} and ${{ matrix.linkage }} + needs: [create-release, prepare] + strategy: + matrix: + platform: [ios, osx, watchos, tvos, catalyst, visionos] + installation: [cocoapods, spm, carthage, xcframework] + linkage: [static, dynamic] + exclude: + - installation: carthage + linkage: static + - platform: catalyst + installation: carthage + - platform: osx + installation: xcframework + linkage: static + - platform: watchos + installation: xcframework + linkage: static + - platform: tvos + installation: xcframework + linkage: static + - platform: catalyst + installation: xcframework + linkage: static + - platform: visionos + installation: xcframework + linkage: static + - platform: catalyst + installation: carthage + linkage: static + - platform: visionos + installation: carthage + - platform: visionos + installation: cocoapods + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install FileUtils + run: gem install fileutils + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: ${{ env.TEST_XCODE_VERSION }} + - name: Set REALM_TEST_RELEASE + run: echo "REALM_TEST_RELEASE=${{ needs.prepare.outputs.VERSION }}" >> $GITHUB_ENV + - name: Run installation test + run: | + cd examples/installation + ./build.rb ${{ matrix.platform }} ${{ matrix.installation }} ${{ matrix.linkage }} + post-slack-release: + runs-on: macos-latest + name: Publish to release Slack channel + needs: [test-installation, prepare] + env: + WEBHOOK_URL: ${{ secrets.SLACK_RELEASES_WEBHOOK }} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Gem install + run: | + gem install octokit + gem install pathname + gem install fileutils + - name: Prepare Changelog + run: ./build.sh prepare-publish-changelog + - name: 'Post to #realm-releases' + uses: realm/ci-actions/release-to-slack@v3 + with: + changelog: ExtractedChangelog/CHANGELOG.md + sdk: Swift + webhook-url: ${{ env.WEBHOOK_URL }} + version: ${{ needs.prepare.outputs.VERSION }} diff --git a/CHANGELOG.md b/CHANGELOG.md index eee15c5252..6f347c8bf9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ x.y.z Release notes (yyyy-MM-dd) * Xcode: 14.2-15.2.0. ### Internal +* Migrated Release pipelines to Github Actions. * Upgraded realm-core from ? to ? 10.46.0 Release notes (2024-01-23) diff --git a/build.sh b/build.sh index ec61df389a..ffb231ca55 100755 --- a/build.sh +++ b/build.sh @@ -26,10 +26,18 @@ if [ -n "${CI}" ]; then CODESIGN_PARAMS=(CODE_SIGN_IDENTITY='' CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO) fi -if [ -n "$CI" ]; then +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" + ROOT_WORKSPACE="$GITHUB_WORKSPACE" + BRANCH="$GITHUB_REF" else - DERIVED_DATA="build/DerivedData/Realm" + ROOT_WORKSPACE="$(pwd)" + DERIVED_DATA="$ROOT_WORKSPACE/build/DerivedData/Realm" + BRANCH="$(git branch --show-current)" fi usage() { @@ -76,17 +84,20 @@ command: set-version version: set the version set-core-version version: set the version of core to use - package platform: build release package for the given platform - package-release: assemble per-platform release packages into a combined one - package-docs: build release package the docs - package-examples: build release package the examples - package-test-examples: test a built examples release package + release-package-examples: build release package the examples + release-package-docs: build release package the docs + release-package-*: build release package for the given platform, configuration and target (this is executed in XCode Cloud) + release-create-xcframework_[xcode-version] [platform]: creates an xcframework from the framework build by the previous step + release-package: creates the final packages + release-package-test-examples: test a built examples release package + test-package-release: locally build a complete release package for all platforms - publish-tag branch: create and push a git tag for the given branch publish-github: create a Github release for the currently checked-out tag publish-docs: publish a built docs release to the website - publish-cocoapods tag: publish the requested tag to CocoaPods + publish-update-checker: publish cocoa file with a version to check for update logic + publish-cocoapods [tag]: publish the requested tag to CocoaPods + prepare-publish-changelog: creates a changelog file to be used in Slack argument: version: version in the x.y.z format @@ -193,6 +204,155 @@ build_combined() { -framework "$os_path" "${simulator_framework[@]}" } +# To be used with Github actions runner +build_platform() { + local product="$1" + local platform="$2" + local config="$CONFIGURATION" + + local destination build_args config_suffix + case "$platform" in + osx) + config_suffix= + destination='generic/platform=macOS' + ;; + ios) + config_suffix=-iphoneos + destination='generic/platform=iOS' + ;; + watchos) + config_suffix=-watchos + destination='generic/platform=watchOS' + ;; + tvos) + config_suffix=-appletvos + destination='generic/platform=tvOS' + ;; + visionos) + config_suffix=-xros + destination='generic/platform=visionOS' + ;; + catalyst) + config_suffix=-maccatalyst + destination='generic/platform=macOS,variant=Mac Catalyst' + ;; + osx) + destination='generic/platform=macOS' + ;; + iossimulator) + config_suffix=-iphonesimulator + destination='generic/platform=iOS' + ;; + watchossimulator) + config_suffix=-watchsimulator + destination='generic/platform=watchOS' + ;; + tvossimulator) + config_suffix=-appletvsimulator + destination='generic/platform=tvOS' + ;; + visionossimulator) + config_suffix=-xrsimulator + destination='generic/platform=visionOS' + ;; + esac + + build_products_path="$DERIVED_DATA/Build/Products" + build_path="$build_products_path/$config${config_suffix}" + + build_args=(-scheme "$product" -configuration "$config" build REALM_HIDE_SYMBOLS=YES) + + if [[ "$platform" = *"simulator" ]]; then + xc -destination "$destination Simulator" "${build_args[@]}" + else + xc -destination "$destination" "${build_args[@]}" + fi + + # This is only for test, and simulates how it is packaged by XCode Cloud + number="$((10000 + $RANDOM % 99999))" + folder_name="RealmSwift Build $number Build Products for $product on iOS" + dir="$folder_name/$config${config_suffix}" + mkdir -p "$dir" + cp -a "$build_path/." "$dir" + + config_name="$(tr [A-Z] [a-z] <<< "$config")" + zip -r product.zip "$dir" + rm -rf "$folder_name" +} + +create_xcframework() { + local product="$1" + local platform="$2" + local config="$3" + local xcode_version="$4" + + config_name="$(tr [A-Z] [a-z] <<< "$config")" + + local destination build_args config_suffix simulator_suffix + case "$platform" in + osx) + config_suffix= + ;; + ios) + config_suffix=-iphoneos + simulator_suffix=iphonesimulator + ;; + watchos) + config_suffix=-watchos + simulator_suffix=watchsimulator + ;; + tvos) + config_suffix=-appletvos + simulator_suffix=appletvsimulator + ;; + visionos) + config_suffix=-xros + simulator_suffix=xrsimulator + ;; + catalyst) + config_suffix=-maccatalyst + ;; + esac + + product_name="$product.framework" + out_path="$ROOT_WORKSPACE/$config/$platform" + xcframework_path="$out_path/$product.xcframework" + os_path="$ROOT_WORKSPACE/product-$platform-$xcode_version-$product-$config_name/$config${config_suffix}/$product_name" + simulator_path="$ROOT_WORKSPACE/product-${platform}simulator-$xcode_version-$product-$config_name/$config-$simulator_suffix/$product_name" + + simulator_framework=() + if [[ -n "$simulator_suffix" ]]; then + simulator_framework+=(-framework "$simulator_path") + fi + + echo "Creating xcframework from paths $os_path, $simulator_path" + xcodebuild -create-xcframework -allow-internal-distribution -output "$xcframework_path" \ + -framework "$os_path" "${simulator_framework[@]}" +} + +unzip_product() { + file_name="$1" + new_file_name="$2" + + unzip "$file_name.zip" -d $file_name + find product -maxdepth 1 -type d -name "RealmSwift Build*" -exec mv {} $new_file_name \; + + rm $file_name.zip + rm -rf $file_name +} + +# Artifacts are zipped by the artifacts store so they're endup nested zipped, so we need to unzip this zip. +unzip_artifact() { + initial_path="$1" + file_name=${initial_path%.*} + + unzip "$file_name.zip" -d "$file_name" + rm "$file_name.zip" + + mv "$file_name/$file_name.zip" "$file_name.zip" + rm -rf "$file_name" +} + clean_retrieve() { mkdir -p "$2" rm -rf "$2/$3" @@ -292,7 +452,7 @@ fi ###################################### COMMAND="$1" -LINKAGE="Dynamic" +LINKAGE="dynamic" # Use Debug config if command ends with -debug, otherwise default to Release case "$COMMAND" in @@ -302,7 +462,7 @@ case "$COMMAND" in ;; *-static) COMMAND="${COMMAND%-static}" - LINKAGE="Static" + LINKAGE="static" CONFIGURATION="Static" ;; esac @@ -485,12 +645,12 @@ case "$COMMAND" in ;; "test-ios") - xctest Realm -configuration "$CONFIGURATION" -sdk iphonesimulator -destination 'name=iPhone 11' + xctest Realm -configuration "$CONFIGURATION" -sdk iphonesimulator -destination 'name=iPhone 14' exit 0 ;; "test-ios-swift") - xctest RealmSwift -configuration "$CONFIGURATION" -sdk iphonesimulator -destination 'name=iPhone 11' + xctest RealmSwift -configuration "$CONFIGURATION" -sdk iphonesimulator -destination 'name=iPhone 14' exit 0 ;; @@ -945,12 +1105,12 @@ case "$COMMAND" in ;; ###################################### - # Continuous Integration + # Continuous Integration PR ###################################### "ci-pr") echo "Building with Xcode Version $(xcodebuild -version)" - export sha="$CI_BRANCH" + export sha="$BRANCH" export REALM_EXTRA_BUILD_ARGUMENTS='GCC_GENERATE_DEBUGGING_SYMBOLS=NO -allowProvisioningUpdates' target=$(echo "$CI_WORKFLOW" | cut -f1 -d_) sh build.sh "verify-$target" @@ -960,135 +1120,230 @@ case "$COMMAND" in # Release packaging ###################################### - "package-docs") - sh scripts/reset-simulators.sh + "release-package-examples") + ./scripts/package_examples.rb + zip --symlinks -r realm-examples.zip examples -x "examples/installation/*" + ;; + + "release-package-docs") sh build.sh docs - cd docs - zip -r realm-docs.zip objc_output swift_output + zip -r docs/realm-docs.zip docs/objc_output docs/swift_output ;; - "package-examples") - ./scripts/package_examples.rb - zip --symlinks -r realm-examples.zip examples -x "examples/installation/*" + "release-package-product") + unzip_product product product-package ;; - "package-build-scripts") - zip -r build-scripts.zip build.sh dependencies.list scripts examples/installation + (release-create-xcframework_*) + platform="$2" + xcode_version=$(echo "$COMMAND" | cut -d_ -f2 ) + + create_xcframework Realm "$platform" Release "$xcode_version" + create_xcframework RealmSwift "$platform" Release "$xcode_version" + + if [ "$platform" = "ios" ] ; then + create_xcframework Realm "$platform" Static "$xcode_version" + else + mkdir -p "Static" + fi + + zip --symlinks -r "realm-$platform-$xcode_version.zip" "Release/$platform" "Static/$platform" + exit 0 ;; - "package-test-examples") - VERSION="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")" - dir="realm-swift-${VERSION}" - unzip "${dir}.zip" + "release-package") + version="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")" + find . -name 'realm-*-1*' -maxdepth 1 \ + | sed 's@./realm-[a-z]*-\(.*\)@\1@' \ + | sort -u --version-sort \ + | xargs ./scripts/create-release-package.rb "${ROOT_WORKSPACE}/pkg" "${version}" + ;; - cp "$0" "${dir}" - cp -r "${source_root}/scripts" "${dir}" - cd "${dir}" + "release-test-examples") + VERSION="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")" + filename="realm-swift-${VERSION}" + unzip "${filename}" + + cp "$0" "${filename}" + cp -r "${source_root}/scripts" "${filename}" + cp "dependencies.list" "${filename}" + + cd "${filename}" sh build.sh examples-ios sh build.sh examples-tvos sh build.sh examples-osx sh build.sh examples-ios-swift sh build.sh examples-tvos-swift cd .. - rm -rf "${dir}" + rm -rf "${filename}" + + exit 0 ;; - "package") - PLATFORM="$2" - sh build.sh "$PLATFORM-swift" - if [[ "$PLATFORM" == ios ]]; then - sh build.sh "$PLATFORM-static" - else - mkdir -p Static - fi + ###################################### + # Release tests + ###################################### - cd build - zip --symlinks -r "realm-$PLATFORM-$REALM_XCODE_VERSION.zip" "Release/$PLATFORM" "Static/$PLATFORM" - ;; + # Should select xcode version `xcode-select` first. + # Pass xcode version as argument + # This simulates what is done in XCode Cloud + ("test-create-frameworks") + xcode_version="$2" + targets="Realm RealmSwift" + + platforms=("ios" "iossimulator" "osx" "tvos" "tvossimulator" "watchos" "watchossimulator" "catalyst") + if [ "$xcode_version" == "15.2" ]; then + platforms+=("visionos" "visionossimulator") + fi - "package-release") - version="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")" - find . -name 'realm-*-1*.zip' -maxdepth 1 \ - | sed 's@./realm-[a-z]*-\(.*\).zip@\1@' \ - | sort -u --version-sort \ - | xargs ./scripts/create-release-package.rb "${WORKSPACE}/pkg" "${version}" + for platform in "${platforms[@]}"; do + for target in $targets; do + echo "Building $platform and $target release" + ./build.sh "release-package-$platform-$xcode_version-$target-release" + ./build.sh "release-build_$platform-$xcode_version-$target-release" + + # Only generates Realm framework for Static configuration and ios platform + if [[ "$platform" == "ios" || "$platform" == "iossimulator" ]] && [[ "$target" == "Realm" ]]; then + echo "Building $platform and $target static" + ./build.sh "release-package-$platform-$xcode_version-$target-static" + ./build.sh "release-package-build_$platform-$xcode_version-$target-static" + fi + done + done ;; - "test-package-release") - # Generate a release package locally for testing purposes - # Real releases should always be done via Jenkins - if [ -z "${WORKSPACE}" ]; then - echo 'WORKSPACE must be set to a directory to assemble the release in' - exit 1 - fi - if [ -d "${WORKSPACE}" ]; then - echo 'WORKSPACE directory should not already exist' + ("test-build-product-workflow-xcode-cloud") + issuer_id="" + key_id="" + pk_path="" + + token=$(ruby ./scripts/xcode_cloud_helper.rb --issuer-id $issuer_id --key-id $key_id --pk-path $pk_path get-token) + echo "Authentication token -> $token" + + # Test parameters + platform="ios" + target="RealmSwift" + xcode_version="15.2" + configuration="release" + + workflow_id=$(ruby ./scripts/xcode_cloud_helper.rb create-workflow release-package-build $platform $xcode_version $target $configuration -t $token) + echo "Created workflow -> $workflow_id" + build_run_id=$(ruby ./scripts/xcode_cloud_helper.rb build-workflow $workflow_id dp/new_migration_branch -t $token) + echo "Build Run -> $build_run_id" + while [ "$status" != 'COMPLETE' ] + do + token=$(ruby ./scripts/xcode_cloud_helper.rb --issuer-id $issuer_id --key-id $key_id --pk-path $pk_path get-token) + status=$(ruby ./scripts/xcode_cloud_helper.rb get-build-status $build_run_id -t $token) + echo "Status $status" | ts + sleep 20 + done + completion_status=$(ruby ./scripts/xcode_cloud_helper.rb get-build-result $build_run_id -t $token) + echo "Completion Status $completion_status" | ts + if [ "$completion_status" != 'SUCCEEDED' ]; then + echo "XCode build failed" + ruby ./scripts/xcode_cloud_helper.rb print-build-logs $build_run_id -t $token exit 1 fi - REALM_SOURCE="$(pwd)" - mkdir -p "$WORKSPACE" - WORKSPACE="$(cd "$WORKSPACE" && pwd)" - export WORKSPACE - cd "$WORKSPACE" - git clone --recursive "$REALM_SOURCE" realm-swift - cd realm-swift - - echo 'Packaging iOS' - sh build.sh package ios - cp "build/realm-ios-$REALM_XCODE_VERSION.zip" . - - echo 'Packaging macOS' - sh build.sh package osx - cp "build/realm-osx-$REALM_XCODE_VERSION.zip" . - - echo 'Packaging watchOS' - sh build.sh package watchos - cp "build/realm-watchos-$REALM_XCODE_VERSION.zip" . - - echo 'Packaging tvOS' - sh build.sh package tvos - cp "build/realm-tvos-$REALM_XCODE_VERSION.zip" . - - echo 'Packaging Catalyst' - sh build.sh package catalyst - cp "build/realm-catalyst-$REALM_XCODE_VERSION.zip" . - - if (( "$(xcode_version_major)" >= 15 )); then - echo 'Packaging visionOS' - sh build.sh package visionOS - cp "build/realm-visionOS-$REALM_XCODE_VERSION.zip" . + ruby ./scripts/xcode_cloud_helper.rb print-build-logs $build_run_id -t $token + ruby ./scripts/xcode_cloud_helper.rb download-artifact $build_run_id -t $token + ruby ./scripts/xcode_cloud_helper.rb delete-workflow $workflow_id -t $token + ;; + + # Pass xcode version as argument + # For this to work, product builds should be located in the root of the project + ("test-create-platform-xcframeworks") + xcode_version="$2" + + platforms=("ios" "osx" "tvos" "watchos" "catalyst") + if [ "$xcode_version" == "15.1" ]; then + platforms+=("visionos") fi - echo 'Packaging examples' - sh build.sh package-examples + for platform in "${platforms[@]}"; do + ./build.sh release-create-xcframework_$xcode_version $platform + + rm -rf "$ROOT_WORKSPACE/Release" + rm -rf "$ROOT_WORKSPACE/Static" + done + ;; + + ("test-package-examples") + VERSION="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")" + dir="realm-swift-${VERSION}" + + # Unzip it + unzip "${dir}.zip" + + # Copy the build.sh file into the downloaded directory + cp "$0" "${dir}" + + # Copy the scripts into the downloaded directory + cp -r "${ROOT_WORKSPACE}/scripts" "${dir}" - echo 'Building final release packages' - export WORKSPACE="${WORKSPACE}/realm-swift" - sh build.sh package-release + # Copy dependencies.list + cp -r "${ROOT_WORKSPACE}/dependencies.list" "${dir}" - echo 'Testing packaged examples' - sh build.sh package-test-examples + cd "${dir}" + # Test Examples + sh build.sh examples-ios + sh build.sh examples-tvos + sh build.sh examples-osx + sh build.sh examples-ios-swift + sh build.sh examples-tvos-swift ;; + # This is used for test or if we want to use Github Actions to build each framework + (release-package-build_*) + filename="Configuration/Release.xcconfig" + sed -i '' "s/REALM_HIDE_SYMBOLS = NO;/REALM_HIDE_SYMBOLS = YES;/" "$filename" + + # Remove the identifier of the command, so we can obtain the parameters from the command + build_command=${COMMAND#"release-package-build_"} + parameters=(${build_command//-/ }) + + platform=${parameters[0]} + target=${parameters[1]} + + build_platform "$target" "$platform" + exit 0 + ;; + + ###################################### + # Publish + ###################################### + "publish-github") + sha="$2" VERSION="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")" - ./scripts/github_release.rb "$VERSION" + + ./scripts/github_release.rb download-artifacts release-package "${sha}" + + unzip release-package.zip -d release-package + + ./scripts/github_release.rb create-release "$VERSION" + exit 0 ;; "publish-docs") + sha="$2" + + ./scripts/github_release.rb download-artifacts realm-docs "${sha}" + unzip_artifact realm-docs.zip + unzip realm-docs.zip + VERSION="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")" PRERELEASE_REGEX='alpha|beta|rc|preview' if [[ $VERSION =~ $PRERELEASE_REGEX ]]; then + echo "Pre-release version" exit 0 fi - rm -rf swift_output objc_output - unzip realm-docs.zip - s3cmd put --recursive --acl-public --access_key=${AWS_ACCESS_KEY_ID} --secret_key=${AWS_SECRET_ACCESS_KEY} swift_output/ s3://realm-sdks/docs/realm-sdks/swift/${VERSION}/ - s3cmd put --recursive --acl-public --access_key=${AWS_ACCESS_KEY_ID} --secret_key=${AWS_SECRET_ACCESS_KEY} swift_output/ s3://realm-sdks/docs/realm-sdks/swift/latest/ - s3cmd put --recursive --acl-public --access_key=${AWS_ACCESS_KEY_ID} --secret_key=${AWS_SECRET_ACCESS_KEY} objc_output/ s3://realm-sdks/docs/realm-sdks/objc/${VERSION}/ - s3cmd put --recursive --acl-public --access_key=${AWS_ACCESS_KEY_ID} --secret_key=${AWS_SECRET_ACCESS_KEY} objc_output/ s3://realm-sdks/docs/realm-sdks/objc/latest/ + s3cmd put --recursive --acl-public --access_key=${AWS_ACCESS_KEY_ID} --secret_key=${AWS_SECRET_ACCESS_KEY} docs/swift_output/ s3://realm-sdks/docs/realm-sdks/swift/${VERSION}/ + s3cmd put --recursive --acl-public --access_key=${AWS_ACCESS_KEY_ID} --secret_key=${AWS_SECRET_ACCESS_KEY} docs/swift_output/ s3://realm-sdks/docs/realm-sdks/swift/latest/ + + s3cmd put --recursive --acl-public --access_key=${AWS_ACCESS_KEY_ID} --secret_key=${AWS_SECRET_ACCESS_KEY} docs/objc_output/ s3://realm-sdks/docs/realm-sdks/objc/${VERSION}/ + s3cmd put --recursive --acl-public --access_key=${AWS_ACCESS_KEY_ID} --secret_key=${AWS_SECRET_ACCESS_KEY} docs/objc_output/ s3://realm-sdks/docs/realm-sdks/objc/latest/ ;; "publish-update-checker") @@ -1100,26 +1355,21 @@ case "$COMMAND" in # update static.realm.io/update/cocoa printf "%s" "${VERSION}" > cocoa - s3cmd put cocoa s3://static.realm.io/update/ - rm cocoa - ;; - - "publish-tag") - git clone git@github.com:realm/realm-swift.git - cd realm-swift - git checkout "$2" - VERSION="$(sed -n 's/^VERSION=\(.*\)$/\1/p' dependencies.list)" - git tag -m "Release ${VERSION}" "v${VERSION}" - git push origin "v${VERSION}" + s3cmd put --recursive --acl-public --access_key=${AWS_ACCESS_KEY_ID} --secret_key=${AWS_SECRET_ACCESS_KEY} cocoa s3://static.realm.io/update/ + exit 0 ;; "publish-cocoapods") - git clone https://github.com/realm/realm-swift - cd realm-swift - git checkout "$2" - ./scripts/reset-simulators.rb + cd "${ROOT_WORKSPACE}" pod trunk push Realm.podspec --verbose --allow-warnings pod trunk push RealmSwift.podspec --verbose --allow-warnings --synchronous + exit 0 + ;; + + "prepare-publish-changelog") + VERSION="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")" + ./scripts/github_release.rb package-release-notes "$VERSION" + exit 0 ;; "add-empty-changelog") diff --git a/ci_scripts/ci_post_clone.sh b/ci_scripts/ci_post_clone.sh index 3c707c0c42..41bc8cb8a6 100755 --- a/ci_scripts/ci_post_clone.sh +++ b/ci_scripts/ci_post_clone.sh @@ -24,8 +24,10 @@ install_dependencies() { elif [[ "$CI_WORKFLOW" == "objectserver"* ]] || [[ "$target" == "swiftpm"* ]]; then sh build.sh setup-baas sh build.sh download-core - elif [[ "$$CI_WORKFLOW" = *"xcode"* ]] || [[ "$target" = "xcframework"* ]]; then + elif [[ "$$CI_WORKFLOW" = *"spm"* ]] || [[ "$target" = "xcframework"* ]]; then install_ruby + elif [[ "$CI_WORKFLOW" == *"carthage"* ]]; then + brew install carthage else sh build.sh download-core fi @@ -76,6 +78,13 @@ if [[ "$target" == *-encryption ]]; then 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 + # 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, diff --git a/examples/installation/build.rb b/examples/installation/build.rb index be8803bce1..fedfa0ee63 100755 --- a/examples/installation/build.rb +++ b/examples/installation/build.rb @@ -1,6 +1,6 @@ #!/usr/bin/env ruby -require 'FileUtils' +require 'fileutils' def usage() puts <<~END @@ -76,12 +76,14 @@ def copy_xcframework(path, framework, dir = '') if not Dir.exist? source raise "Missing XCFramework to test at '#{source}'" end + + puts "Copying xcframework from #{source} into ../../build/#{dir}" sh 'cp', '-cR', source, "../../build/#{dir}" end def download_release(version) # Download and extract the zip if the extracted directory doesn't already - # exist. For CI release testing, we already have a local copy of the zip that + # exist. For master-push workflow testing, we already downloaded a local copy of the zip that # just needs to be extracted. unless Dir.exist? "realm-swift-#{version}" unless File.exist? "realm-swift-#{version}.zip" @@ -118,11 +120,6 @@ def download_realm(platform, method, static) end File.write 'Cartfile', 'github "realm/realm-swift"' + version - # Carthage requires that a simulator exist, but `xcodebuild -list` is - # sometimes very slow if too many simulators exist, so delete all but one - # per platform - sh '../../scripts/reset-simulators.rb', '-firstOnly' - platformName = case platform when 'ios' then 'iOS' when 'osx' then 'Mac' @@ -165,10 +162,10 @@ def download_realm(platform, method, static) download_release version else if static - copy_xcframework '../../build/Static', 'Realm' + copy_xcframework "../../build/Static/#{platform}", 'Realm', 'Static' else - copy_xcframework '../../build/Release', 'Realm' - copy_xcframework '../../build/Release', 'RealmSwift' + copy_xcframework "../../build/Release/#{platform}", 'Realm' + copy_xcframework "../../build/Release/#{platform}", 'RealmSwift' end end @@ -232,7 +229,7 @@ def test(platform, method, linkage = 'dynamic') ENV.delete 'REALM_BUILD_STATIC' end - puts "Testing #{method} for #{platform}" + puts "Testing #{method} for #{platform} and #{linkage}" download_realm(platform, method, static) build_app(platform, method, static) diff --git a/examples/ios/objc/RealmExamples.xcodeproj/project.pbxproj b/examples/ios/objc/RealmExamples.xcodeproj/project.pbxproj index 22022bc701..90f4dfcbed 100644 --- a/examples/ios/objc/RealmExamples.xcodeproj/project.pbxproj +++ b/examples/ios/objc/RealmExamples.xcodeproj/project.pbxproj @@ -1241,6 +1241,242 @@ }; name = Release; }; + AC1CA6392B1FF0BE002167B0 /* Static */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; + CONFIGURATION_TEMP_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + OTHER_LDFLAGS = "-lz"; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Static; + }; + AC1CA63A2B1FF0BE002167B0 /* Static */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_WARN_UNREACHABLE_CODE = YES; + DEVELOPMENT_TEAM = ""; + ENABLE_STRICT_OBJC_MSGSEND = YES; + INFOPLIST_FILE = "$(SRCROOT)/Backlink/Backlink-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "io.realm.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Static; + }; + AC1CA63B2B1FF0BE002167B0 /* Static */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ENABLE_MODULES = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CODE_SIGN_ENTITLEMENTS = Draw/Draw.entitlements; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = "$(SRCROOT)/Draw/Draw-Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = "-lz"; + PRODUCT_BUNDLE_IDENTIFIER = io.realm.Draw; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Draw/Draw-Bridging-Header.h"; + }; + name = Static; + }; + AC1CA63C2B1FF0BE002167B0 /* Static */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = ""; + INFOPLIST_FILE = "Encryption/Encryption-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "io.realm.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Static; + }; + AC1CA63D2B1FF0BE002167B0 /* Static */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_WARN_UNREACHABLE_CODE = YES; + CODE_SIGN_ENTITLEMENTS = Extension/Extension.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; + ENABLE_STRICT_OBJC_MSGSEND = YES; + INFOPLIST_FILE = Extension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "io.realm.examples.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + }; + name = Static; + }; + AC1CA63E2B1FF0BE002167B0 /* Static */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_WARN_UNREACHABLE_CODE = YES; + CODE_SIGN_ENTITLEMENTS = TodayExtension/TodayExtension.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEVELOPMENT_TEAM = ""; + ENABLE_STRICT_OBJC_MSGSEND = YES; + INFOPLIST_FILE = TodayExtension/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "io.realm.examples.extension.$(PRODUCT_NAME:rfc1034identifier)-2"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + SKIP_INSTALL = YES; + }; + name = Static; + }; + AC1CA63F2B1FF0BE002167B0 /* Static */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = ""; + INFOPLIST_FILE = "$(SRCROOT)/GroupedTableView/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + OTHER_LDFLAGS = ( + "-lz", + "-ObjC", + ); + PRODUCT_BUNDLE_IDENTIFIER = "io.realm.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = GroupedTableView; + WRAPPER_EXTENSION = app; + }; + name = Static; + }; + AC1CA6402B1FF0BE002167B0 /* Static */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = ""; + INFOPLIST_FILE = "$(SRCROOT)/RACTableView/Info.plist"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$PODS_CONFIGURATION_BUILD_DIR\"", + "\"$PODS_CONFIGURATION_BUILD_DIR/ReactiveCocoa\"", + ); + PRODUCT_BUNDLE_IDENTIFIER = "io.realm.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = RACTableView; + WRAPPER_EXTENSION = app; + }; + name = Static; + }; + AC1CA6412B1FF0BE002167B0 /* Static */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = ""; + INFOPLIST_FILE = "Migration/Migration-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "io.realm.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Static; + }; + AC1CA6422B1FF0BE002167B0 /* Static */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = ""; + INFOPLIST_FILE = "REST/REST-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "io.realm.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Static; + }; + AC1CA6432B1FF0BE002167B0 /* Static */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = ""; + INFOPLIST_FILE = "Simple/Simple-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "io.realm.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Static; + }; + AC1CA6442B1FF0BE002167B0 /* Static */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = ""; + INFOPLIST_FILE = "TableView/TableView-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "io.realm.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Static; + }; C0DC41ED1A7072680067156A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -1401,8 +1637,8 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)-static"; - CONFIGURATION_TEMP_DIR = "$(PROJECT_TEMP_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)-static"; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; + CONFIGURATION_TEMP_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -1459,8 +1695,8 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)-static"; - CONFIGURATION_TEMP_DIR = "$(PROJECT_TEMP_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)-static"; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; + CONFIGURATION_TEMP_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -1726,6 +1962,7 @@ buildConfigurations = ( 3F78C9C51B431CF300A0988E /* Debug */, 3F78C9C61B431CF300A0988E /* Release */, + AC1CA6402B1FF0BE002167B0 /* Static */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -1735,6 +1972,7 @@ buildConfigurations = ( C0DC41ED1A7072680067156A /* Debug */, C0DC41EE1A7072680067156A /* Release */, + AC1CA63D2B1FF0BE002167B0 /* Static */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -1744,6 +1982,7 @@ buildConfigurations = ( C0DC42151A7079D00067156A /* Debug */, C0DC42161A7079D00067156A /* Release */, + AC1CA63E2B1FF0BE002167B0 /* Static */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -1753,6 +1992,7 @@ buildConfigurations = ( E879D9D61A12AA120035E2EB /* Debug */, E879D9D71A12AA120035E2EB /* Release */, + AC1CA63F2B1FF0BE002167B0 /* Static */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -1762,6 +2002,7 @@ buildConfigurations = ( E8AB71D019BA502500F3EDB4 /* Debug */, E8AB71D119BA502500F3EDB4 /* Release */, + AC1CA6392B1FF0BE002167B0 /* Static */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -1771,6 +2012,7 @@ buildConfigurations = ( E8AB720119BA503500F3EDB4 /* Debug */, E8AB720219BA503500F3EDB4 /* Release */, + AC1CA6432B1FF0BE002167B0 /* Static */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -1780,6 +2022,7 @@ buildConfigurations = ( E8AB722F19BA503B00F3EDB4 /* Debug */, E8AB723019BA503B00F3EDB4 /* Release */, + AC1CA6412B1FF0BE002167B0 /* Static */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -1789,6 +2032,7 @@ buildConfigurations = ( E8AB725D19BA504000F3EDB4 /* Debug */, E8AB725E19BA504000F3EDB4 /* Release */, + AC1CA63C2B1FF0BE002167B0 /* Static */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -1798,6 +2042,7 @@ buildConfigurations = ( E8AB728B19BA504500F3EDB4 /* Debug */, E8AB728C19BA504500F3EDB4 /* Release */, + AC1CA6442B1FF0BE002167B0 /* Static */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -1807,6 +2052,7 @@ buildConfigurations = ( E8AB72B919BA504900F3EDB4 /* Debug */, E8AB72BA19BA504900F3EDB4 /* Release */, + AC1CA6422B1FF0BE002167B0 /* Static */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -1816,6 +2062,7 @@ buildConfigurations = ( E8BDBFC11A116FAC00450CFF /* Debug */, E8BDBFC21A116FAC00450CFF /* Release */, + AC1CA63A2B1FF0BE002167B0 /* Static */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; @@ -1825,6 +2072,7 @@ buildConfigurations = ( F18464311DC151CD00DAB8B9 /* Debug */, F18464321DC151CD00DAB8B9 /* Release */, + AC1CA63B2B1FF0BE002167B0 /* Static */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/scripts/create-release-package.rb b/scripts/create-release-package.rb index 0ee6ada455..ca4662478e 100755 --- a/scripts/create-release-package.rb +++ b/scripts/create-release-package.rb @@ -56,7 +56,7 @@ def zip(name, *files) FileUtils.mkdir_p "#{tmp}/#{version}" Dir.chdir("#{tmp}/#{version}") do for platform in platforms(version) - sh 'unzip', "#{ROOT}/realm-#{platform}-#{version}.zip" + sh 'unzip', "#{ROOT}/realm-#{platform}-#{version}/realm-#{platform}-#{version}.zip" end end end @@ -74,7 +74,7 @@ def zip(name, *files) package_dir = "#{tmp}/realm-swift-#{VERSION}" FileUtils.mkdir_p package_dir sh 'cp', "#{ROOT}/LICENSE", package_dir - sh 'unzip', "#{ROOT}/realm-examples.zip", '-d', package_dir + sh 'unzip', "#{ROOT}/realm-examples/realm-examples.zip", '-d', package_dir for lang in %w(objc swift) File.write "#{package_dir}/#{lang}-docs.webloc", %Q{ @@ -115,7 +115,7 @@ def zip(name, *files) tmp = File.realpath tmp Dir.chdir(tmp) do for platform in platforms('15.1') - sh 'unzip', "#{ROOT}/realm-#{platform}-#{OBJC_XCODE_VERSION}.zip" + sh 'unzip', "#{ROOT}/realm-#{platform}-#{OBJC_XCODE_VERSION}/realm-#{platform}-#{OBJC_XCODE_VERSION}.zip" end create_xcframework tmp, '', 'Release', 'RealmSwift' create_xcframework tmp, '', 'Release', 'Realm' @@ -125,4 +125,3 @@ def zip(name, *files) end end end - diff --git a/scripts/github_release.rb b/scripts/github_release.rb index 8888f8e4a7..4235cfd6fd 100755 --- a/scripts/github_release.rb +++ b/scripts/github_release.rb @@ -2,18 +2,17 @@ require 'pathname' require 'octokit' - -raise 'usage: github_release.rb version' unless ARGV.length == 1 - -VERSION = ARGV[0] -ACCESS_TOKEN = ENV['GITHUB_ACCESS_TOKEN'] -raise 'GITHUB_ACCESS_TOKEN must be set to create GitHub releases' unless ACCESS_TOKEN +require 'fileutils' BUILD_SH = Pathname(__FILE__).+('../../build.sh').expand_path -RELEASE = "v#{VERSION}" REPOSITORY = 'realm/realm-swift' +def sh(*args) + puts "executing: #{args.join(' ')}" if false + system(*args, false ? {} : {:out => '/dev/null'}) || exit(1) +end + def release_notes(version) changelog = BUILD_SH.parent.+('CHANGELOG.md').readlines current_version_index = changelog.find_index { |line| line =~ (/^#{Regexp.escape version}/) } @@ -29,17 +28,61 @@ def release_notes(version) relevant.join.strip end -RELEASE_NOTES = release_notes(VERSION) +def create_release(version) + access_token = ENV['GITHUB_ACCESS_TOKEN'] + raise 'GITHUB_ACCESS_TOKEN must be set to create GitHub releases' unless access_token + + release_notes = release_notes(version) + github = Octokit::Client.new + github.access_token = ENV['GITHUB_ACCESS_TOKEN'] + + puts 'Creating GitHub release' + prerelease = (version =~ /alpha|beta|rc|preview/) ? true : false + release = "v#{version}" + response = github.create_release(REPOSITORY, release, name: release, body: release_notes, prerelease: prerelease) + release_url = response[:url] + + Dir.glob 'release-package/*.zip' do |upload| + puts "Uploading #{upload} to GitHub" + github.upload_asset(release_url, upload, content_type: 'application/zip') + end +end + +def package_release_notes(version) + release_notes = release_notes(version) + FileUtils.mkdir_p("ExtractedChangelog") + out_file = File.new("ExtractedChangelog/CHANGELOG.md", "w") + out_file.puts(release_notes) +end -github = Octokit::Client.new -github.access_token = ENV['GITHUB_ACCESS_TOKEN'] +def download_artifacts(key, sha) + access_token = ENV['GITHUB_ACCESS_TOKEN'] + raise 'GITHUB_ACCESS_TOKEN must be set to create GitHub releases' unless access_token -puts 'Creating GitHub release' -prerelease = (VERSION =~ /alpha|beta|rc|preview/) ? true : false -response = github.create_release(REPOSITORY, RELEASE, name: RELEASE, body: RELEASE_NOTES, prerelease: prerelease) -release_url = response[:url] + github = Octokit::Client.new + github.auto_paginate = true + github.access_token = ENV['GITHUB_ACCESS_TOKEN'] -Dir.glob 'build/*.zip' do |upload| - puts "Uploading #{upload} to GitHub" - github.upload_asset(release_url, upload, content_type: 'application/zip') + response = github.repository_artifacts(REPOSITORY) + sha_artifacts = response[:artifacts].filter { |artifact| artifact[:workflow_run][:head_sha] == sha && artifact[:name] == key } + sha_artifacts.each { |artifact| + download_url = github.artifact_download_url(REPOSITORY, artifact[:id]) + download(artifact[:name], download_url) + } end + +def download(name, url) + sh 'curl', '--output', "#{name}.zip", "#{url}" +end + +if ARGV[0] == 'create-release' + version = ARGV[1] + create_release(version) +elsif ARGV[0] == 'package-release-notes' + version = ARGV[1] + package_release_notes(version) +elsif ARGV[0] == 'download-artifacts' + key = ARGV[1] + sha = ARGV[2] + download_artifacts(key, sha) +end \ No newline at end of file diff --git a/scripts/release-matrix.rb b/scripts/release-matrix.rb new file mode 100755 index 0000000000..21740c20b6 --- /dev/null +++ b/scripts/release-matrix.rb @@ -0,0 +1,77 @@ +#!/usr/bin/env ruby +# Matrix of resulting build actions for each release workflow + +BuildDestination = Struct.new(:build_platform, :destination) do |cls| + def cls.macOS + BuildDestination.new('MACOS', 'ANY_MAC') + end + + def cls.catalyst + BuildDestination.new('MACOS', 'ANY_MAC_CATALYST') + end + + def cls.iOS + BuildDestination.new('IOS', 'ANY_IOS_DEVICE') + end + + def cls.iOSSimulator + BuildDestination.new('IOS', 'ANY_IOS_SIMULATOR') + end + + def cls.tvOS + BuildDestination.new('TVOS', 'ANY_TVOS_DEVICE') + end + + def cls.tvOSSimulator + BuildDestination.new('TVOS', 'ANY_TVOS_SIMULATOR') + end + + def cls.watchOS + BuildDestination.new('WATCHOS', 'ANY_WATCHOS_DEVICE') + end + + def cls.watchOSSimulator + BuildDestination.new('WATCHOS', 'ANY_WATCHOS_SIMULATOR') + end + + def cls.visionOS + BuildDestination.new('VISIONOS', 'ANY_VISIONOS_DEVICE') + end + + def cls.visionOSSimulator + BuildDestination.new('VISIONOS', 'ANY_VISIONOS_SIMULATOR') + end + + def cls.generic + BuildDestination.new('MACOS', nil) + end +end + +RELEASE_DESTINATIONS = { "osx" => BuildDestination.macOS, + "catalyst" => BuildDestination.catalyst, + "ios" => BuildDestination.iOS, + "iossimulator" => BuildDestination.iOSSimulator, + "tvos" => BuildDestination.tvOS, + "tvossimulator" => BuildDestination.tvOSSimulator, + "watchos" => BuildDestination.watchOS, + "watchossimulator" => BuildDestination.watchOSSimulator, + "visionos" => BuildDestination.visionOS, + "visionossimulator" => BuildDestination.visionOSSimulator + } + +ReleaseTarget = Struct.new(:name, :scheme, :platform) do + def action + build_destination = RELEASE_DESTINATIONS[platform] + action = { + name: self.name, + actionType: 'BUILD', + destination: build_destination.destination, + buildDistributionAudience: nil, + scheme: self.scheme, + platform: build_destination.build_platform, + isRequiredToPass: true + } + + return action + end +end \ No newline at end of file diff --git a/scripts/reset-simulators.rb b/scripts/reset-simulators.rb deleted file mode 100755 index 888df37e74..0000000000 --- a/scripts/reset-simulators.rb +++ /dev/null @@ -1,191 +0,0 @@ -#!/usr/bin/ruby - -require 'json' -require 'open3' - -prelaunch_simulator = ARGV[0] || '' - -def platform_for_runtime(runtime) - runtime['identifier'].gsub(/com.apple.CoreSimulator.SimRuntime.([^-]+)-.*/, '\1') -end - -def platform_for_device_type(device_type) - case device_type['identifier'] - when /Watch/ - 'watchOS' - when /TV/ - 'tvOS' - else - 'iOS' - end -end - -def simctl(args) - # When running on a machine with Xcode 11 installed, Xcode 10 sometimes - # incorrectly thinks that it has not completed its first-run installation. - # This results in it printing errors related to that to stdout in front of - # the actual JSON output that we want. - Open3.popen3('xcrun simctl ' + args) do |stdin, stdout, strerr, wait_thr| - while line = stdout.gets - if not line.start_with? 'Install' - return line + stdout.read, wait_thr.value.exitstatus - end - end - end -end - -def wait_for_core_simulator_service - # Run until we get a result since switching simulator versions often causes CoreSimulatorService to throw an exception. - while simctl('list devices')[0].empty? - end -end - -def running_devices(devices) - devices.select { |device| device['state'] != 'Shutdown' } -end - -def shutdown_simulator_devices(devices) - # Shut down any simulators that need it. - running_devices(devices).each do |device| - puts "Shutting down simulator #{device['udid']}" - system("xcrun simctl shutdown #{device['udid']}") or puts " Failed to shut down simulator #{device['udid']}" - end -end - -attempts = 0 -begin - # Kill all the current simulator processes as they may be from a different Xcode version - print 'Killing running Simulator processes...' - while system('pgrep -q Simulator') - system('pkill Simulator 2>/dev/null') - system('pkill -9 update_dyld_sim_shared_cache 2>/dev/null') - # CoreSimulatorService doesn't exit when sent SIGTERM - system('pkill -9 Simulator 2>/dev/null') - end - wait_for_core_simulator_service - puts ' done!' - - print 'Shut down existing simulator devices...' - # Shut down any running simulator devices. This may take multiple attempts if some - # simulators are currently in the process of booting or being created. - all_available_devices = [] - (0..5).each do |shutdown_attempt| - begin - devices_json = simctl('list devices -j')[0] - all_devices = JSON.parse(devices_json)['devices'].flat_map { |_, devices| devices } - rescue JSON::ParserError - sleep shutdown_attempt if shutdown_attempt > 0 - next - end - - # Exclude devices marked as unavailable as they're from a different version of Xcode. - all_available_devices = all_devices.reject { |device| device['availability'] =~ /unavailable/ } - - break if running_devices(all_available_devices).empty? - - shutdown_simulator_devices all_available_devices - sleep shutdown_attempt if shutdown_attempt > 0 - end - puts ' done!' - - # Delete all simulators. - print 'Deleting all simulators...' - (0..5).each do |delete_attempt| - break if all_available_devices.empty? - - all_available_devices.each do |device| - simctl("delete #{device['udid']}") - end - - begin - devices_json = simctl('list devices -j')[0] - all_devices = JSON.parse(devices_json)['devices'].flat_map { |_, devices| devices } - rescue JSON::ParserError - sleep shutdown_attempt if shutdown_attempt > 0 - next - end - - all_available_devices = all_devices.reject { |device| device['availability'] =~ /unavailable/ } - break if all_available_devices.empty? - end - puts ' done!' - - if not all_available_devices.empty? - raise "Failed to delete devices #{all_available_devices}" - end - - # Recreate all simulators. - runtimes = JSON.parse(simctl('list runtimes -j')[0])['runtimes'] - device_types = JSON.parse(simctl('list devicetypes -j')[0])['devicetypes'] - - runtimes_by_platform = Hash.new { |hash, key| hash[key] = [] } - runtimes.each do |runtime| - next unless runtime['availability'] == '(available)' || runtime['isAvailable'] == true - runtimes_by_platform[platform_for_runtime(runtime)] << runtime - end - - firstOnly = prelaunch_simulator == '-firstOnly' - - print 'Creating fresh simulators...' - device_types.each do |device_type| - platform = platform_for_device_type(device_type) - runtimes_by_platform[platform].each do |runtime| - output, ec = simctl("create '#{device_type['name']}' '#{device_type['identifier']}' '#{runtime['identifier']}' 2>&1") - if ec == 0 - if firstOnly - # We only want to create a single simulator for each device type so - # skip the rest. - runtimes_by_platform[platform] = [] - break - else - next - end - end - - # Not all runtime and device pairs are valid as newer simulator runtimes - # don't support older devices. The exact error code for this changes - # every few versions of Xcode, so this just lists all the ones we've - # seen. - next if /domain=com.apple.CoreSimulator.SimError, code=(?\d+)/ =~ output and [161, 162, 163, 403].include? code.to_i - - puts "Failed to create device of type #{device_type['identifier']} with runtime #{runtime['identifier']}:" - output.each_line do |line| - puts " #{line}" - end - end - end - puts ' done!' - - if firstOnly - exit 0 - end - - if prelaunch_simulator.include? 'tvos' - print 'Booting Apple TV simulator...' - system("xcrun simctl boot 'Apple TV'") or raise "Failed to boot Apple TV simulator" - else - print 'Booting iPhone 11 simulator...' - system("xcrun simctl boot 'iPhone 11'") or raise "Failed to boot iPhone 11 simulator" - end - puts ' done!' - - print 'Waiting for dyld shared cache to update...' - 10.times do - break unless system('pgrep -q update_dyld_sim_shared_cache') - sleep 15 - end - puts ' done!' - -rescue => e - if (attempts += 1) < 5 - puts '' - puts e.message - e.backtrace.each { |line| puts line } - puts '' - puts 'Retrying...' - retry - end - system('ps auxwww') - system('xcrun simctl list') - raise -end diff --git a/scripts/reset-simulators.sh b/scripts/reset-simulators.sh deleted file mode 100755 index 70a329918e..0000000000 --- a/scripts/reset-simulators.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -set -o pipefail -set -e - -source "$(dirname "$0")/swift-version.sh" -set_xcode_version - -"$(dirname "$0")/reset-simulators.rb" "$1" diff --git a/scripts/xcode_cloud_helper.rb b/scripts/xcode_cloud_helper.rb index 97a46715ae..10a3cd506a 100755 --- a/scripts/xcode_cloud_helper.rb +++ b/scripts/xcode_cloud_helper.rb @@ -8,17 +8,32 @@ 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 - response = HTTP.request(req) + 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 @@ -39,11 +54,16 @@ def post(path, body) JSON.parse(response.body) end -def get_jwt_bearer(issuer_id, key_id, pk_path) - private_key = OpenSSL::PKey.read(File.read(pk_path)) +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 + 10 * 60, + exp: Time.now.to_i + 20 * 60, aud: 'appstoreconnect-v1' } header_fields = { kid: key_id } @@ -92,14 +112,42 @@ def get_xcode_versions }] 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) result = post('ciWorkflows', create_workflow_request(target, xcode_version)) id = result["data"]["id"] - puts "#{result['data']['attributes']['name']}: https://appstoreconnect.apple.com/teams/69a6de86-7f37-47e3-e053-5b8c7c11a4d1/frameworks/#{get_realm_product_id}/workflows/#{id}" return id end @@ -182,10 +230,10 @@ def delete_workflow(id) req = Net::HTTP::Delete.new("/v1/ciWorkflows/#{id}") req['Content-type'] = 'application/json' response = request req - puts "Workflow deleted #{id}" end -def start_build(id) +def start_build(id, branch) + branch_id = find_git_reference_for_branch(branch) result = post('ciBuildRuns', { data: { type: 'ciBuildRuns', @@ -196,13 +244,17 @@ def start_build(id) type: 'ciWorkflows', id: id } + }, + sourceBranchOrTag: { + data: { + type: 'scmGitReferences', + id: branch_id + } } } } }) id = result['data']['id'] - puts "Workflow build started with id: #{id}:" - puts result return id end @@ -222,8 +274,6 @@ def synchronize_workflows() } current_workflows = get_workflows.filter_map { |workflow| name = workflow['attributes']['name'] - # don't touch release pipeline jobs - next if name.include? 'release_' pieces = name.partition('_') {name: pieces.first, version: pieces.last, id: workflow['id']} } @@ -266,10 +316,89 @@ def synchronize_workflows() end workflows_to_create.each { |w| - create_workflow(w[:target], w[:version]) + id = create_workflow(w[:target], w[:version]) + 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', "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', "product.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 @@ -307,14 +436,14 @@ def get_workflow_id_for_name(name) $workflows_list.find { |w| w['attributes']['name'].split('_')[0] == name }['id'] end -Options = Struct.new(:token, :team_id, :issuer_id, :key_id, :pk_path) +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 to automatically create a token. + and --pk-path or --pk to automatically create a token. Commands: list-workflows @@ -333,6 +462,22 @@ def get_workflow_id_for_name(name) 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. @@ -355,6 +500,9 @@ def get_workflow_id_for_name(name) 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! @@ -363,9 +511,14 @@ def usage() exit 1 end -JWT_TOKEN = options[:token] || begin - usage if not options[:issuer_id] or not options[:key_id] or not options[:pk_path] - get_jwt_bearer(options[:issuer_id], options[:key_id], options[:pk_path]) +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 @@ -394,8 +547,42 @@ def usage() synchronize_workflows() when 'build-workflow' workflow_id = ARGV.shift + branch = ARGV.shift usage unless workflow_id - start_build(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) + 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' - pp JWT_TOKEN -end + puts JWT_TOKEN +end \ No newline at end of file