diff --git a/.github/workflows/master-push.yml b/.github/workflows/master-push.yml
new file mode 100644
index 00000000000..38db4c3ab49
--- /dev/null
+++ b/.github/workflows/master-push.yml
@@ -0,0 +1,211 @@
+name: Prepare Release
+on:
+ push:
+ branches:
+ - "dp/migration_release_pipeline"
+jobs:
+ prepare:
+ runs-on: ubuntu-latest
+ name: Create Draft Release to store artifacts
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Export GITHUB_TOKEN to workspace
+ run: echo "GITHUB_ACCESS_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV
+ - name: Install ruby
+ uses: ruby/setup-ruby@ec02537da5712d66d4d50a0f33b7eb52773b5ed1
+ with:
+ ruby-version: '3.1.2'
+ - name: Install Gems
+ run: |
+ gem install octokit
+ - name: Create draft release to store artifacts.
+ run: ruby ./scripts/github_release.rb create-draft-release
+ build-docs:
+ runs-on: ubuntu-latest
+ name: Package docs and upload them to the draft release
+ needs: prepare
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Get Token
+ id: token
+ uses: yuki0n0/action-appstoreconnect-token@v1.0
+ with:
+ # UUID. Can get from App Store Connect.
+ issuer id: ${{ secrets.APPLE_STORE_CONNECT_ISSUER_ID }}
+ # Key ID. Can get from App Store Connect.
+ key id: ${{ secrets.APPLE_STORE_CONNECT_KEY_ID }}
+ # P8 private key. Can get from App Store Connect.
+ key: ${{ secrets.APPLE_STORE_CONNECT_API_KEY }}
+ - name: Run release_package-docs xcode cloud build and wait for it to finish, this xcode cloud upload realm-docs.zip assets to the artifacts draft release
+ run: |
+ XCODE_VERSION="$(ruby ./scripts/release-matrix.rb docs_version)"
+ ruby ./scripts/xcode_cloud_helper.rb --run-release-workflow "release_package-docs_${XCODE_VERSION}" --token ${{ steps.token.outputs.token }}
+ build-examples:
+ runs-on: macos-latest
+ name: Package examples and upload them to the draft release
+ needs: prepare
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Export GITHUB_TOKEN to workspace
+ run: echo "GITHUB_ACCESS_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV
+ - name: Install ruby
+ uses: ruby/setup-ruby@ec02537da5712d66d4d50a0f33b7eb52773b5ed1
+ with:
+ ruby-version: '3.1.2'
+ - name: Install Gems
+ run: |
+ gem install octokit
+ gem install xcodeproj
+ - name: Run release_package-examples package and uploads the example folder
+ run: ./build.sh release_package-examples
+ build-product:
+ runs-on: ubuntu-latest
+ name: Package product for each xcode version.
+ needs: build-examples
+ strategy:
+ matrix:
+ xcode-version: ['14.1', '14.2', '14.3.1', '15.0.1', '15.1']
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Get Token
+ id: token
+ uses: yuki0n0/action-appstoreconnect-token@v1.0
+ with:
+ # UUID. Can get from App Store Connect.
+ issuer id: ${{ secrets.APPLE_STORE_CONNECT_ISSUER_ID }}
+ # Key ID. Can get from App Store Connect.
+ key id: ${{ secrets.APPLE_STORE_CONNECT_KEY_ID }}
+ # P8 private key. Can get from App Store Connect.
+ key: ${{ secrets.APPLE_STORE_CONNECT_API_KEY }}
+ - name: Run release_package_${{ matrix.xcode-version }} which upload the package to the draft release and wait for it to complete or fail
+ run: ruby ./scripts/xcode_cloud_helper.rb --run-release-workflow "release_package_${{ matrix.xcode-version }}" --token ${{ steps.token.outputs.token }}
+ test-package-examples:
+ runs-on: ubuntu-latest
+ name: Test examples, using the packages releases
+ needs: build-product
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Get Token
+ id: token
+ uses: yuki0n0/action-appstoreconnect-token@v1.0
+ with:
+ # UUID. Can get from App Store Connect.
+ issuer id: ${{ secrets.APPLE_STORE_CONNECT_ISSUER_ID }}
+ # Key ID. Can get from App Store Connect.
+ key id: ${{ secrets.APPLE_STORE_CONNECT_KEY_ID }}
+ # P8 private key. Can get from App Store Connect.
+ key: ${{ secrets.APPLE_STORE_CONNECT_API_KEY }}
+ - name: Run release_test-package-examples_15.1 which runs test over the packages produces by the previous step and wait for it to complete or fail
+ run: ruby ./scripts/xcode_cloud_helper.rb --run-release-workflow release_test-package-examples_15.1 --token ${{ steps.token.outputs.token }}
+ test-ios-static:
+ runs-on: ubuntu-latest
+ name: Run tests on iOS with configuration Static
+ needs: build-product
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Get Token
+ id: token
+ uses: yuki0n0/action-appstoreconnect-token@v1.0
+ with:
+ # UUID. Can get from App Store Connect.
+ issuer id: ${{ secrets.APPLE_STORE_CONNECT_ISSUER_ID }}
+ # Key ID. Can get from App Store Connect.
+ key id: ${{ secrets.APPLE_STORE_CONNECT_KEY_ID }}
+ # P8 private key. Can get from App Store Connect.
+ key: ${{ secrets.APPLE_STORE_CONNECT_API_KEY }}
+ - name: Run release_ios-static_15.1 which runs test over the packages produces by the previous step and wait for it to complete or fail
+ run: ruby ./scripts/xcode_cloud_helper.rb --run-release-workflow release_test-ios-static_15.1 --token ${{ steps.token.outputs.token }}
+ test-osx-static:
+ runs-on: ubuntu-latest
+ name: Run tests on macOS
+ needs: build-product
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Get Token
+ id: token
+ uses: yuki0n0/action-appstoreconnect-token@v1.0
+ with:
+ # UUID. Can get from App Store Connect.
+ issuer id: ${{ secrets.APPLE_STORE_CONNECT_ISSUER_ID }}
+ # Key ID. Can get from App Store Connect.
+ key id: ${{ secrets.APPLE_STORE_CONNECT_KEY_ID }}
+ # P8 private key. Can get from App Store Connect.
+ key: ${{ secrets.APPLE_STORE_CONNECT_API_KEY }}
+ - name: Run release_osx_15.1 which runs test over the packages produces by the previous step and wait for it to complete or fail
+ run: ruby ./scripts/xcode_cloud_helper.rb --run-release-workflow release_test-osx_15.1 --token ${{ steps.token.outputs.token }}
+ test-installation:
+ runs-on: ubuntu-latest
+ name: Run installation test on each xcode version
+ needs: build-product
+ env:
+ XCODE_VERSION: '14.3.1'
+ strategy:
+ matrix:
+ platform: [ios, osx, watchos, tvos, catalyst]
+ installation: [cocoapods, spm, carthage, xcframework]
+ linkage: [static, dynamic]
+ exclude:
+ - platform: catalyst
+ installation: carthage
+ - installation: carthage
+ linkage: static
+ - installation: xcframework
+ linkage: static
+ - platform: osx
+ installation: xcframework
+ linkage: static
+ - platform: watchos
+ installation: xcframework
+ linkage: static
+ - platform: tvos
+ installation: xcframework
+ linkage: static
+ - platform: catalyst
+ installation: xcframework
+ linkage: static
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Get Token
+ id: token
+ uses: yuki0n0/action-appstoreconnect-token@v1.0
+ with:
+ # UUID. Can get from App Store Connect.
+ issuer id: ${{ secrets.APPLE_STORE_CONNECT_ISSUER_ID }}
+ # Key ID. Can get from App Store Connect.
+ key id: ${{ secrets.APPLE_STORE_CONNECT_KEY_ID }}
+ # P8 private key. Can get from App Store Connect.
+ key: ${{ secrets.APPLE_STORE_CONNECT_API_KEY }}
+ - name: Creates an XCode Cloud workflow to run the test and runs build
+ run: |
+ ruby ./scripts/xcode_cloud_helper.rb --create-release-workflow-and-run test-installation-${{ matrix.platform }}-${{ matrix.installation }}-${{ matrix.linkage }} --xcode-version "$XCODE_VERSION" --token ${{ steps.token.outputs.token }} --team-id ${{ secrets.APPLE_STORE_CONNECT_TEAM_ID }}
+ test-installation-xcode:
+ runs-on: ubuntu-latest
+ name: Run installation test on macOS only for for xcframework for all xcode versions
+ needs: build-product
+ strategy:
+ matrix:
+ xcode_version: ['14.1', '14.2', '14.3.1', '15.0.1', '15.1']
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Get Token
+ id: token
+ uses: yuki0n0/action-appstoreconnect-token@v1.0
+ with:
+ # UUID. Can get from App Store Connect.
+ issuer id: ${{ secrets.APPLE_STORE_CONNECT_ISSUER_ID }}
+ # Key ID. Can get from App Store Connect.
+ key id: ${{ secrets.APPLE_STORE_CONNECT_KEY_ID }}
+ # P8 private key. Can get from App Store Connect.
+ key: ${{ secrets.APPLE_STORE_CONNECT_API_KEY }}
+ - name: Creates an XCode Cloud workflow to run the test and runs build
+ run: |
+ ruby ./scripts/xcode_cloud_helper.rb --create-release-workflow-and-run test-installation-osx-xcframework-dynamic --xcode-version ${{ matrix.xcode_version }} --token ${{ steps.token.outputs.token }} --team-id ${{ secrets.APPLE_STORE_CONNECT_TEAM_ID }}
\ 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 00000000000..d55d71ec01b
--- /dev/null
+++ b/.github/workflows/publish-release.yml
@@ -0,0 +1,167 @@
+name: Publish release
+on: workflow_dispatch
+jobs:
+ prepare:
+ runs-on: macos-latest
+ name: Check if draft release is created
+ steps:
+ - name: Get draft release
+ id: get_release
+ uses: agners/get-draft-release@v0.1.0
+ env:
+ GITHUB_TOKEN: ${{ github.token }}
+ tag-release:
+ runs-on: macos-latest
+ name: Tag Release
+ needs: prepare
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Extract branch name
+ shell: bash
+ run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
+ id: extract_branch
+ - name: Tag release
+ run: ./build.sh publish-tag ${{ steps.extract_branch.outputs.branch }}
+ publish-docs:
+ runs-on: macos-latest
+ name: Publish docs to S3 Bucket
+ needs: prepare
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Export AWS Secrets to workspace
+ run: |
+ echo "AWS_ACCESS_KEY_ID=${{ secrets.AWS_ACCESS_KEY_ID }}" >> $GITHUB_ENV
+ echo "AWS_SECRET_ACCESS_KEY=${{ secrets.AWS_SECRET_ACCESS_KEY }}" >> $GITHUB_ENV
+ - name: Download docs and upload them to S3
+ run: |
+ ./build.sh publish-docs
+ create-release:
+ runs-on: macos-latest
+ name: Create github release
+ needs: prepare
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - 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
+ publish-cocoapods:
+ runs-on: macos-latest
+ name: Publish Cocoapods specs
+ needs: prepare
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Read SDK version
+ id: get-version
+ run: |
+ version="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")"pkgVersion=$(find . -type f -regex ".*Realm.[1-9].*.nupkg" -exec basename {} \; | sed -n 's/Realm\.\(.*\)\.nupkg$/\1/p')
+ echo "version=$version" >> $GITHUB_OUTPUT
+ - name: Export COCOAPODS_TRUNK_TOKEN to workspace
+ run: echo "COCOAPODS_TRUNK_TOKEN=${{ secrets.COCOAPODS_TRUNK_TOKEN }}" >> $GITHUB_ENV
+ - name: Publish
+ run: ./build.sh publish-cocoapods v${{ steps.get-version.outputs.version }}
+ update-update-checker:
+ runs-on: macos-latest
+ name: Publish Cocoapods specs
+ needs: prepare
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Export AWS Secrets to workspace
+ run: |
+ echo "AWS_ACCESS_KEY_ID=${{ secrets.UPDATE_CHECKER_ACCESS_KEY }}" >> $GITHUB_ENV
+ echo "AWS_SECRET_ACCESS_KEY=${{ secrets.UPDATE_CHECKER_SECRET_KEY }}" >> $GITHUB_ENV
+ - name: Create Github release
+ run: ./build.sh publish-update-checker
+ test-installation:
+ runs-on: ubuntu-latest
+ name: Run installation test on each xcode version
+ needs: update-update-checker
+ env:
+ XCODE_VERSION: '15.1'
+ strategy:
+ matrix:
+ platform: [ios, osx, watchos, tvos, catalyst, visionos]
+ installation: [cocoapods, spm, carthage, xcframework]
+ linkage: [static, dynamic]
+ exclude:
+ - installation: carthage
+ linkage: static
+ - 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: Get Token
+ id: token
+ uses: yuki0n0/action-appstoreconnect-token@v1.0
+ with:
+ # UUID. Can get from App Store Connect.
+ issuer id: ${{ secrets.APPLE_STORE_CONNECT_ISSUER_ID }}
+ # Key ID. Can get from App Store Connect.
+ key id: ${{ secrets.APPLE_STORE_CONNECT_KEY_ID }}
+ # P8 private key. Can get from App Store Connect.
+ key: ${{ secrets.APPLE_STORE_CONNECT_API_KEY }}
+ - name: Creates an XCode Cloud workflow to run the test and runs build
+ run: |
+ ruby ./scripts/xcode_cloud_helper.rb --create-release-workflow-and-run test-installation-${{ matrix.platform }}-${{ matrix.installation }}-${{ matrix.linkage }} --xcode-version "$XCODE_VERSION" --token ${{ steps.token.outputs.token }} --team-id ${{ secrets.APPLE_STORE_CONNECT_TEAM_ID }}
+ post-release-channel:
+ runs-on: macos-latest
+ name: Publish to release Slack channel
+ needs: test-installation
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Prepare Changelog
+ run: echo "Diana"
+ - name: Read SDK version
+ id: get-version
+ run: |
+ version="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")"pkgVersion=$(find . -type f -regex ".*Realm.[1-9].*.nupkg" -exec basename {} \; | sed -n 's/Realm\.\(.*\)\.nupkg$/\1/p')
+ echo "version=$version" >> $GITHUB_OUTPUT
+ - name: 'Post to #realm-releases'
+ uses: realm/ci-actions/release-to-slack@fa20eb972b9f018654fdb4e2c7afb52b0532f907
+ with:
+ changelog: Realm/packages/ExtractedChangelog/ExtractedChangelog.md
+ sdk: .Swift
+ webhook-url: ${{ secrets.SLACK_RELEASE_WEBHOOK }}
+ version: ${{ steps.get-version.outputs.version }}
+ add-empty-changelog:
+ runs-on: macos-latest
+ name: Add empty changelog and push it to master, preparing for a future release
+ needs: post-release-channel
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: Add empty changelog
+ run: ./build.sh add-empty-changelog
+ - name: Commit & Push changes to master
+ uses: actions-js/push@master
+ with:
+ github_token: ${{ github.token }}
+
+
\ No newline at end of file
diff --git a/build.sh b/build.sh
index 886a7224c5b..d57093dba44 100755
--- a/build.sh
+++ b/build.sh
@@ -31,11 +31,11 @@ fi
if [ -n "$CI" ]; then
DERIVED_DATA="$CI_DERIVED_DATA_PATH"
- ROOT_WORKSPACE="$CI_WORKSPACE/"
+ ROOT_WORKSPACE="$CI_WORKSPACE"
BRANCH="$CI_BRANCH"
else
DERIVED_DATA="build/DerivedData/Realm"
- ROOT_WORKSPACE=""
+ ROOT_WORKSPACE="$(pwd)"
BRANCH="$(git branch --show-current)"
fi
@@ -187,7 +187,7 @@ build_combined() {
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"
- local out_path="build/$config/$platform"
+ local out_path="$ROOT_WORKSPACE/$config/$platform"
local xcframework_path="$out_path/$product.xcframework"
# Build for each platform
@@ -308,7 +308,7 @@ fi
######################################
COMMAND="$1"
-LINKAGE="Dynamic"
+LINKAGE="dynamic"
# Use Debug config if command ends with -debug, otherwise default to Release
case "$COMMAND" in
@@ -318,7 +318,7 @@ case "$COMMAND" in
;;
*-static)
COMMAND="${COMMAND%-static}"
- LINKAGE="Static"
+ LINKAGE="static"
CONFIGURATION="Static"
;;
esac
@@ -992,6 +992,12 @@ case "$COMMAND" in
exit 0
;;
+ "prepare-release-changelog")
+ realm_version="$2"
+
+ exit 0
+ ;;
+
"set-core-version")
new_version="$2"
old_version="$(sed -n 's/^REALM_CORE_VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")"
@@ -1069,58 +1075,110 @@ 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/*"
+ ./scripts/github_prepare.rb --upload-product realm-examples.zip --path "${GITHUB_WORKSPACE}/realm-examples.zip"
+ ;;
+
+ "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
+ ./scripts/github_prepare.rb --upload-product realm-docs.zip --path "${ROOT_WORKSPACE}/docs/realm-docs.zip"
;;
- "package-examples")
+ ("release_package")
+ XCODE_VERSION="$2"
+ PLATFORMS=$(./scripts/release-matrix.rb plaforms_for_version $XCODE_VERSION)
+ PLATFORMS_ARRAY=(${PLATFORMS//,/ })
+
+ # Package examples for package
./scripts/package_examples.rb
zip --symlinks -r realm-examples.zip examples -x "examples/installation/*"
- ;;
- "package-build-scripts")
- zip -r build-scripts.zip build.sh dependencies.list scripts examples/installation
+ # Create frameworks for each platform and zip it
+ for platform in ${PLATFORMS_ARRAY[@]}; do
+ sh build.sh "$platform-swift"
+ if [[ "$platform" == ios ]]; then
+ sh build.sh "$platform-static"
+ else
+ mkdir -p build/Static
+ fi
+
+ FILE_NAME="realm-${platform}-${XCODE_VERSION}"
+ zip --symlinks -r "${FILE_NAME}.zip" "Release/${platform}" "Static/${platform}"
+ done
+
+ # Create Realm(Static)/RealmSwift XCFrameworks zips combining all the platforms frameworks
+ 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 create-version-xcframeworks "${ROOT_WORKSPACE}/pkg" "${VERSION}" "${XCODE_VERSION}"
+
+
+ echo "Uploading data into draft release"
+ # Upload RealmSwift zips to draft release
+ ./scripts/github_prepare.rb --upload-product "realm-swift-${VERSION}_${XCODE_VERSION}.zip" --path "${ROOT_WORKSPACE}/pkg/realm-swift-${VERSION}_${XCODE_VERSION}.zip"
+ ./scripts/github_prepare.rb --upload-product "RealmSwift@${XCODE_VERSION}.spm.zip" --path "${ROOT_WORKSPACE}/pkg/RealmSwift@${XCODE_VERSION}.spm.zip"
+
+ # Upload Realm zips to draft release, only for latest xcode version
+ if [ -f "${ROOT_WORKSPACE}pkg/Realm.spm.zip" ]; then
+ ./scripts/github_prepare.rb --upload-product "Realm.spm.zip" --path "${ROOT_WORKSPACE}pkg/Realm.spm.zip"
+ ./scripts/github_prepare.rb --upload-product "Carthage.xcframework.zip" --path "${ROOT_WORKSPACE}/pkg/Carthage.xcframework.zip"
+ fi
;;
- "package-test-examples")
+ ("release_test-package-examples")
VERSION="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")"
+ XCODE_VERSION=$(echo "$COMMAND" | cut -d_ -f2-)
+
dir="realm-swift-${VERSION}"
+
+ # Download package for current xcode version
+ ./scripts/github_prepare.rb --download-asset "${dir}_${REALM_XCODE_VERSION}.zip" --path "${ROOT_WORKSPACE}/${dir}.zip"
+
+ # Unzip it
unzip "${dir}.zip"
+ # Copy the build.sh file into the downloaded directory
cp "$0" "${dir}"
- cp -r "${source_root}/scripts" "${dir}"
+
+ # Copy the scripts into the downloaded directory
+ cp -r "${ROOT_WORKSPACE}/scripts" "${dir}"
+
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
- cd ..
- rm -rf "${dir}"
;;
- "package")
- PLATFORM="$2"
- sh build.sh "$PLATFORM-swift"
- if [[ "$PLATFORM" == ios ]]; then
- sh build.sh "$PLATFORM-static"
- else
- mkdir -p Static
- fi
+ ("release_test-ios")
+ echo "Test run on test action"
+ ;;
+
+ ("release_test-osx")
+ echo "Test run on test action"
+ ;;
- cd build
- zip --symlinks -r "realm-$PLATFORM-$REALM_XCODE_VERSION.zip" "Release/$PLATFORM" "Static/$PLATFORM"
+ (release_test-installation-*)
+ target=(${COMMAND//-/ })
+ platform="${target[2]}"
+ installation="${target[3]}"
+ VERSION="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")"
+
+ cd examples/installation
+
+ # When the target finish in static $LINKAGE variable changes to static
+ echo "Testing installation ${installation} method in ${platform} for ${LINKAGE}"
+ REALM_TEST_RELEASE="$VERSION" ./build.rb "${platform}" "${installation}" "${LINKAGE}"
;;
- "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}"
+ "package-build-scripts")
+ zip -r build-scripts.zip build.sh dependencies.list scripts examples/installation
;;
"test-package-release")
@@ -1182,7 +1240,39 @@ case "$COMMAND" in
"publish-github")
VERSION="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")"
- ./scripts/github_release.rb "$VERSION"
+ XCODE_VERSIONS=$(./scripts/release-matrix.rb xcode_versions)
+ XCODE_VERSIONS_ARRAY=(${XCODE_VERSIONS//,/ })
+
+ # Create binary directory to store all the data to upload.
+ package_dir=release_pkg
+ mkdir -p "${package_dir}"
+
+ # Download RealmSwift.xcframework/Realm.xcframework for each xcode version and pack it in a package containig all of them, created in the prepare step.
+ file_name=realm-swift-${VERSION}
+ for xcode_version in ${XCODE_VERSIONS_ARRAY[@]}; do
+ ./scripts/github_prepare.rb --download-asset "${file_name}_${xcode_version}.zip" --path "${ROOT_WORKSPACE}/${file_name}_${xcode_version}.zip"
+
+ unzip -o "${file_name}_${xcode_version}.zip"
+ done
+
+ zip --symlinks -r "${ROOT_WORKSPACE}/${package_dir}/${file_name}.zip" "${file_name}"
+
+ # Download SPM files for each xcode version, created in the prepare step.
+ for xcode_version in ${XCODE_VERSIONS_ARRAY[@]}; do
+ spm_file_name=RealmSwift@${xcode_version}.spm.zip
+ ./scripts/github_prepare.rb --download-asset "${spm_file_name}" --path "${ROOT_WORKSPACE}/${package_dir}/${spm_file_name}"
+ done
+
+ # Download the Obj-C xcframework created in the prepare step.
+ realm_file_name=Realm.spm.zip
+ ./scripts/github_prepare.rb --download-asset "${realm_file_name}" --path "${ROOT_WORKSPACE}/${package_dir}/${realm_file_name}"
+
+ # Download the Carthage xcframework
+ carthage_file_name=Carthage.xcframework.zip
+ ./scripts/github_prepare.rb --download-asset "${carthage_file_name}" --path "${ROOT_WORKSPACE}/${package_dir}/${carthage_file_name}"
+
+ # Prepare version for
+ ./scripts/github_release.rb "10.45.0"
;;
"publish-docs")
@@ -1191,8 +1281,12 @@ case "$COMMAND" in
if [[ $VERSION =~ $PRERELEASE_REGEX ]]; then
exit 0
fi
- rm -rf swift_output objc_output
+
+ # Download docs from the draft release
+ ./scripts/github_prepare.rb --download-asset "realm-docs.zip" --path "${ROOT_WORKSPACE}/realm-docs.zip"
+
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/
@@ -1209,24 +1303,23 @@ case "$COMMAND" in
# update static.realm.io/update/cocoa
printf "%s" "${VERSION}" > cocoa
- s3cmd put cocoa s3://static.realm.io/update/
- rm cocoa
+ s3cmd put --recursive --acl-public --access_key=${AWS_ACCESS_KEY_ID} --secret_key=${AWS_SECRET_ACCESS_KEY} cocoa s3://static.realm.io/update/
;;
"publish-tag")
git clone git@github.com:realm/realm-swift.git
cd realm-swift
- git checkout "$2"
+ git checkout "$BRANCH"
VERSION="$(sed -n 's/^VERSION=\(.*\)$/\1/p' dependencies.list)"
git tag -m "Release ${VERSION}" "v${VERSION}"
git push origin "v${VERSION}"
;;
"publish-cocoapods")
+ VERSION="$(sed -n 's/^VERSION=\(.*\)$/\1/p' "${source_root}/dependencies.list")"
git clone https://github.com/realm/realm-swift
cd realm-swift
- git checkout "$2"
- ./scripts/reset-simulators.rb
+ git checkout "$VERSION"
pod trunk push Realm.podspec --verbose --allow-warnings
pod trunk push RealmSwift.podspec --verbose --allow-warnings --synchronous
;;
diff --git a/ci_scripts/ci_post_clone.sh b/ci_scripts/ci_post_clone.sh
index a986768d8bf..ce489bcf0ab 100755
--- a/ci_scripts/ci_post_clone.sh
+++ b/ci_scripts/ci_post_clone.sh
@@ -13,8 +13,7 @@ install_dependencies() {
echo ">>> Installing dependencies for ${CI_WORKFLOW}"
brew install moreutils
-
- if [[ "$CI_WORKFLOW" == "docs"* ]]; then
+ if [[ "$CI_WORKFLOW" == "docs"* ]] || [[ "$CI_WORKFLOW" == *"docs"* ]]; then
install_ruby
gem install jazzy -v ${JAZZY_VERSION}
elif [[ "$CI_WORKFLOW" == "swiftlint"* ]]; then
@@ -22,19 +21,38 @@ install_dependencies() {
elif [[ "$CI_WORKFLOW" == "cocoapods"* ]]; then
install_ruby
gem install cocoapods -v ${COCOAPODS_VERSION}
- elif [[ "$$CI_WORKFLOW" = *"xcode"* ]] || [[ "$target" = "xcframework"* ]]; then
+ elif [[ "$$CI_WORKFLOW" == *"xcode"* ]] || [[ "$target" == "xcframework"* ]]; then
install_ruby
fi
+
+ if [[ "$CI_WORKFLOW" == "release"* ]]; then
+ echo "Installing release gems"
+ install_ruby
+ gem install octokit
+ gem install getoptlong
+ gem install xcodeproj
+ fi
+
+ if [[ "$CI_WORKFLOW" == *"package_15.1" ]]; then
+ # We need to install the visionOS because is not installed by default in the XCode Cloud image,
+ # even if the build action selected platform is visionOS.
+ echo "Installing visionos"
+ xcodebuild -downloadPlatform visionOS
+ fi
}
+INSTALLED=0
install_ruby() {
# Ruby Installation
- echo ">>> Installing new Version of ruby"
- brew install rbenv ruby-build
- rbenv install ${RUBY_VERSION}
- rbenv global ${RUBY_VERSION}
- echo 'export PATH=$HOME/.rbenv/bin:$PATH' >>~/.bash_profile
- eval "$(rbenv init -)"
+ if [[ "$INSTALLED" == 0 ]]; then
+ echo ">>> Installing new Version of ruby"
+ brew install rbenv ruby-build
+ rbenv install ${RUBY_VERSION}
+ rbenv global ${RUBY_VERSION}
+ echo 'export PATH=$HOME/.rbenv/bin:$PATH' >>~/.bash_profile
+ eval "$(rbenv init -)"
+ INSTALLED=1
+ fi
}
update_scheme_configuration() {
@@ -77,11 +95,19 @@ install_dependencies
# CI Workflows
cd ..
-# Get target name
-TARGET=$(echo "$CI_WORKFLOW" | cut -f1 -d_)
-
-# Update schemes configuration
-update_scheme_configuration ${TARGET}
-
-export target="${TARGET}"
-sh -x build.sh ci-pr | ts
\ No newline at end of file
+if [[ "$CI_WORKFLOW" == "release"* ]]; then
+ echo "Release workflow"
+ TARGET="${CI_WORKFLOW%_*}"
+ XCODE_VERSION=${CI_WORKFLOW##*_}
+ sh -x build.sh "${TARGET}" "${XCODE_VERSION}" | ts
+else
+ # CI PR Pipelines, use this path
+ # Get target name
+ TARGET=$(echo "$CI_WORKFLOW" | cut -f1 -d_)
+
+ # Update schemes configuration
+ update_scheme_configuration ${TARGET}
+
+ export target="${TARGET}"
+ sh -x build.sh ci-pr | ts
+fi
diff --git a/scripts/create-release-package.rb b/scripts/create-release-package.rb
index 06793349809..27ac681ef67 100755
--- a/scripts/create-release-package.rb
+++ b/scripts/create-release-package.rb
@@ -3,16 +3,19 @@
require 'fileutils'
require 'pathname'
require 'tmpdir'
+require_relative "release-matrix"
-raise 'usage: create-release-package.rb destination_path version [xcode_versions]' unless ARGV.length >= 3
+include RELEASE
-DESTINATION = Pathname(ARGV[0])
-VERSION = ARGV[1]
-XCODE_VERSIONS = ARGV[2..]
+raise 'usage: create-release-package.rb destination_path version [xcode_version]' unless ARGV.length >= 3
+
+DESTINATION = Pathname(ARGV[1])
+VERSION = ARGV[2]
+XCODE_VERSION = ARGV[3]
ROOT = Pathname(__FILE__).+('../..').expand_path
BUILD_SH = Pathname(__FILE__).+('../../build.sh').expand_path
VERBOSE = false
-OBJC_XCODE_VERSION = XCODE_VERSIONS.last
+OBJC_XCODE_VERSION = RELEASE::XCODE_VERSIONS.last.partition(" ").first
def sh(*args)
puts "executing: #{args.join(' ')}" if VERBOSE
@@ -31,7 +34,7 @@ def create_xcframework(root, xcode_version, configuration, name)
prefix = "#{root}/#{xcode_version}"
output = "#{prefix}/#{configuration}/#{name}.xcframework"
files = Dir.glob "#{prefix}/#{configuration}/*/#{name}.xcframework/*/#{name}.framework"
-
+ puts "Creating xcframework to #{output} from #{prefix}/#{configuration}/*/#{name}.xcframework/*/#{name}.framework returning files #{files}"
sh 'xcodebuild', '-create-xcframework', '-allow-internal-distribution',
'-output', output, *files.flat_map {|f| ['-framework', f]}
end
@@ -39,93 +42,127 @@ def create_xcframework(root, xcode_version, configuration, name)
def zip(name, *files)
path = (DESTINATION + name).to_path
FileUtils.rm_f path
+ puts "Zipping file #{name} into #{path}"
sh 'zip', '--symlinks', '-r', path, *files
end
-puts "Packaging version #{VERSION} for Xcode versions #{XCODE_VERSIONS.join(', ')}"
-FileUtils.mkdir_p DESTINATION
-
-Dir.mktmpdir do |tmp|
- # The default temp directory is in /var, which is a symlink to /private/var
- # xcodebuild's relative path resolution breaks due to this and we need to
- # give it the fully resolved path
- tmp = File.realpath tmp
-
- for version in XCODE_VERSIONS
- puts "Extracting source binaries for Xcode #{version}"
- FileUtils.mkdir_p "#{tmp}/#{version}"
- Dir.chdir("#{tmp}/#{version}") do
- for platform in platforms(version)
- sh 'unzip', "#{ROOT}/realm-#{platform}-#{version}.zip"
+def create_version_xcframeworks
+ puts "Packaging version #{VERSION} for Xcode version #{XCODE_VERSION}"
+ FileUtils.mkdir_p DESTINATION
+
+ Dir.mktmpdir do |tmp|
+ # The default temp directory is in /var, which is a symlink to /private/var
+ # xcodebuild's relative path resolution breaks due to this and we need to
+ # give it the fully resolved path
+ tmp = File.realpath tmp
+
+ # Extracts/Unzip all the binarios for each of the platforms for the current XCode version into a temp
+ # folder, which is gonna be used to generate the zip for each XCode version.
+ puts "Extracting source binaries for Xcode #{XCODE_VERSION}"
+ FileUtils.mkdir_p "#{tmp}/#{XCODE_VERSION}"
+ Dir.chdir("#{tmp}/#{XCODE_VERSION}") do
+ for platform in platforms(XCODE_VERSION)
+ puts "Unziping #{ROOT}/realm-#{platform}-#{XCODE_VERSION}.zip into #{tmp}/#{XCODE_VERSION}"
+ sh 'unzip', "#{ROOT}/realm-#{platform}-#{XCODE_VERSION}.zip"
end
end
- end
- for version in XCODE_VERSIONS
- puts "Creating Swift XCFrameworks for Xcode #{version}"
- create_xcframework tmp, version, 'Release', 'RealmSwift'
- end
+ # Creates a RealmSwift.xcframework from each platform framework for any supported architecture (device and simulator).
+ puts "Creating Swift XCFrameworks for Xcode #{XCODE_VERSION}"
+ create_xcframework tmp, XCODE_VERSION, 'Release', 'RealmSwift'
- puts 'Creating Obj-C XCFrameworks'
- create_xcframework tmp, OBJC_XCODE_VERSION, 'Release', 'Realm'
- create_xcframework tmp, OBJC_XCODE_VERSION, 'Static', 'Realm'
-
- puts 'Creating release package'
- 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
- for lang in %w(objc swift)
- File.write "#{package_dir}/#{lang}-docs.webloc", %Q{
-
-
-
-
- URL
- https://www.mongodb.com/docs/realm-sdks/${lang}/${version}
-
-
- }
- end
- sh 'cp', '-Rca', "#{tmp}/#{OBJC_XCODE_VERSION}/Release/Realm.xcframework", "#{package_dir}"
- FileUtils.mkdir_p "#{package_dir}/static"
- sh 'cp', '-Rca', "#{tmp}/#{OBJC_XCODE_VERSION}/Static/Realm.xcframework", "#{package_dir}/static"
- for version in XCODE_VERSIONS
- FileUtils.mkdir_p "#{package_dir}/#{version}"
- sh 'cp', '-Rca', "#{tmp}/#{version}/Release/RealmSwift.xcframework", "#{package_dir}/#{version}"
- end
+ if XCODE_VERSION == OBJC_XCODE_VERSION
+ # Creates a Realm.xcframework from each platform framework for any supported architecture (device and simulator).
+ puts 'Creating Obj-C XCFrameworks, only for latest xcode version'
+ create_xcframework tmp, XCODE_VERSION, 'Release', 'Realm'
+ # Creates a Static Realm.xcframework from each platform framework for any supported architecture (device and simulator).
+ create_xcframework tmp, XCODE_VERSION, 'Static', 'Realm'
+ end
- Dir.chdir(tmp) do
- zip "realm-swift-#{VERSION}.zip", "realm-swift-#{VERSION}"
- end
+ package_dir = "#{tmp}/realm-swift-#{VERSION}"
+ FileUtils.mkdir_p package_dir
+ puts "Creating release package on temp #{tmp}/realm-swift-#{VERSION} from generated xcframeworks"
+
+ sh 'cp', "#{ROOT}/LICENSE", package_dir
+ sh 'unzip', "#{ROOT}/realm-examples.zip", '-d', package_dir
+
+ for lang in %w(objc swift)
+ File.write "#{package_dir}/#{lang}-docs.webloc", %Q{
+
+
+
+
+ URL
+ https://www.mongodb.com/docs/realm-sdks/${lang}/${version}
+
+
+ }
+ end
- puts 'Creating SPM release zips'
- Dir.chdir "#{tmp}/#{OBJC_XCODE_VERSION}/Release" do
- zip 'Realm.spm.zip', "Realm.xcframework"
- end
- for version in XCODE_VERSIONS
- Dir.chdir "#{tmp}/#{version}/Release" do
- zip "RealmSwift@#{version}.spm.zip", 'RealmSwift.xcframework'
+ # Copy the generated RealmSwift.xcframework into a temp directory following this notation realm-swift-#{VERSION}/#{XCODE_VERSION}/
+ puts "Copying Release XCFramework into a #{XCODE_VERSION} folder"
+ FileUtils.mkdir_p "#{package_dir}/#{XCODE_VERSION}"
+ sh 'cp', '-Rca', "#{tmp}/#{XCODE_VERSION}/Release/RealmSwift.xcframework", "#{package_dir}/#{XCODE_VERSION}"
+
+ if XCODE_VERSION == OBJC_XCODE_VERSION
+ # Copy the generated Realm.xcframework into a temp directory following this notation realm-swift-#{VERSION}
+ puts 'Copying Release Obj-C XCFramework, only for latest xcode version'
+ sh 'cp', '-Rca', "#{tmp}/#{XCODE_VERSION}/Release/Realm.xcframework", "#{package_dir}"
+
+ # Copy the generated Static Realm.xcframework into a temp directory following this notation realm-swift-#{VERSION}/static
+ puts 'Copying Static Obj-C XCFramework, only for latest xcode version'
+ FileUtils.mkdir_p "#{package_dir}/static"
+ sh 'cp', '-Rca', "#{tmp}/#{XCODE_VERSION}/Static/Realm.xcframework", "#{package_dir}/static"
end
- end
-end
-# Our normal Xcode 15 xcframework includes visionOS slices build with a beta
-# version of Xcode, but Carthage doesn't like that so we have to build a
-# separate xcframework without visionOS
-puts 'Creating Carthage release zip'
-Dir.mktmpdir do |tmp|
- tmp = File.realpath tmp
- Dir.chdir(tmp) do
- for platform in platforms('14')
- sh 'unzip', "#{ROOT}/realm-#{platform}-#{OBJC_XCODE_VERSION}.zip"
+ # Zip the generated RealmSwift/Realm(Static) xcframework into a generated realm-swift-#{VERSION}.zip
+ puts 'Packing all the xcframework into a zip, only for latest xcode version'
+ Dir.chdir(tmp) do
+ zip "realm-swift-#{VERSION}_#{XCODE_VERSION}.zip", "realm-swift-#{VERSION}"
end
- create_xcframework tmp, '', 'Release', 'RealmSwift'
- create_xcframework tmp, '', 'Release', 'Realm'
- Dir.chdir('Release') do
- zip 'Carthage.xcframework.zip', 'Realm.xcframework', 'RealmSwift.xcframework'
+ # Zip generated RealmSwift.xcframework into a generated RealmSwift@#{XCODE_VERSION}.spm.zip
+ Dir.chdir "#{tmp}/#{XCODE_VERSION}/Release" do
+ zip "RealmSwift@#{XCODE_VERSION}.spm.zip", 'RealmSwift.xcframework'
+ end
+
+ if XCODE_VERSION == OBJC_XCODE_VERSION
+ Dir.chdir "#{tmp}/#{XCODE_VERSION}/Release" do
+ # Zip generated Realm.xcframework into a generated Realm.spm.zip
+ puts 'Packing Obj-C xcframework into a zip'
+ zip "Realm.spm.zip", "Realm.xcframework"
+ end
+ end
+ end
+
+ if XCODE_VERSION == OBJC_XCODE_VERSION
+ # Our normal Xcode 15 xcframework includes visionOS slices build with a beta
+ # version of Xcode, but Carthage doesn't like that so we have to build a
+ # separate xcframework without visionOS
+ puts 'Creating Carthage release zip'
+ Dir.mktmpdir do |tmp|
+ tmp = File.realpath tmp
+ FileUtils.mkdir_p "#{tmp}/#{XCODE_VERSION}"
+ Dir.chdir("#{tmp}/#{XCODE_VERSION}") do
+ for platform in platforms('14')
+ puts "unziping #{ROOT}/realm-#{platform}-#{OBJC_XCODE_VERSION}.zip into #{tmp}"
+ sh 'unzip', "#{ROOT}/realm-#{platform}-#{OBJC_XCODE_VERSION}.zip"
+ end
+ end
+
+ puts "Creating xcframework in #{tmp}"
+ create_xcframework tmp, XCODE_VERSION, 'Release', 'RealmSwift'
+ create_xcframework tmp, XCODE_VERSION, 'Release', 'Realm'
+
+ Dir.chdir "#{tmp}/#{XCODE_VERSION}/Release" do
+ puts "Zipping Carthage.xcframework.zip"
+ zip 'Carthage.xcframework.zip', 'Realm.xcframework', 'RealmSwift.xcframework'
+ end
end
end
end
+if ARGV[0] == 'create-version-xcframeworks'
+ create_version_xcframeworks
+end
+
diff --git a/scripts/github_prepare.rb b/scripts/github_prepare.rb
new file mode 100755
index 00000000000..f5ec6b01b55
--- /dev/null
+++ b/scripts/github_prepare.rb
@@ -0,0 +1,128 @@
+#!/usr/bin/env ruby
+
+require 'octokit'
+require 'getoptlong'
+require 'uri'
+require 'open-uri'
+require 'fileutils'
+
+require_relative "release-matrix"
+
+include RELEASE
+
+ACCESS_TOKEN = ENV['GITHUB_ACCESS_TOKEN']
+raise 'GITHUB_ACCESS_TOKEN must be set to create GitHub releases' unless ACCESS_TOKEN
+
+REPOSITORY = 'realm/realm-swift'
+
+def update_package_asset(path, name, content_type)
+ github = Octokit::Client.new
+ github.access_token = ENV['GITHUB_ACCESS_TOKEN']
+
+ response_get_releases = github.releases(REPOSITORY)
+ draft_release = response_get_releases.select {|release| release[:name] == 'Artifacts release' && release[:draft] == true }
+ puts "Draft release founded #{draft_release}"
+
+ puts "Uploading asset #{name} for path: #{path}"
+ release_url = draft_release[0][:url]
+ puts "Release Url #{release_url}"
+ response_upload_asset = github.upload_asset(release_url, path, { :name => "#{name}", :content_type => content_type })
+ puts response_upload_asset
+end
+
+def upload_product(name, path)
+ puts "Uploading #{name} from #{path}"
+ update_package_asset(path, name, 'application/zip')
+end
+
+def download_artifact(name, path)
+ github = Octokit::Client.new
+ github.access_token = ENV['GITHUB_ACCESS_TOKEN']
+
+ response_get_release = github.releases(REPOSITORY)
+ draft_release = response_get_release.select {|release| release[:name] == 'Artifacts release' && release[:draft] == true }
+ puts "Draft release founded #{draft_release}"
+
+ release_url = draft_release[0][:url]
+ puts "Release Url #{release_url}"
+ response_current_assets = github.release_assets(release_url)
+
+ puts "Find asset #{name}"
+ asset = response_current_assets.find{ |asset| asset[:name] == name }
+
+ puts "Downloading asset #{asset[:url]}"
+ download(asset[:url], path)
+end
+
+def download(url, path)
+ open(path, 'wb') do |file|
+ uri = URI.parse(url)
+ io = uri.open("Authorization" => "Bearer #{ENV['GITHUB_ACCESS_TOKEN']}",
+ "Accept" => "application/octet-stream",
+ )
+ puts "Writing from temp #{io.path} to #{path}"
+ case io
+ when StringIO then
+ File.open(path, 'w') { |f| f.write(io.read) }
+ when Tempfile then
+ io.close; FileUtils.mv(io.path, path)
+ end
+ end
+end
+
+opts = GetoptLong.new(
+ [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
+ [ '--path', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--upload-product', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--download-asset', GetoptLong::REQUIRED_ARGUMENT ]
+)
+
+option = ''
+name = ''
+path = ''
+
+opts.each do |opt, arg|
+ if opt != '--path'
+ option = opt
+ end
+ case opt
+ when '--help'
+ puts <<-EOF
+hello [OPTION] ...
+
+-h, --help:
+ show help
+
+--upload-product
+ Upload docs to the draft release
+
+ EOF
+ exit
+ when '--path'
+ if arg == ''
+ raise "Path is required to execute this"
+ else
+ path = arg
+ end
+ when '--upload-product', '--download-asset'
+ if arg == ''
+ raise "Name is required to execute this"
+ else
+ name = arg
+ end
+ end
+end
+
+if option == '--upload-product'
+ if name == '' || path == ''
+ raise 'Missing product name or path.'
+ else
+ upload_product(name, path)
+ end
+elsif option == '--download-asset'
+ if name == '' || path == ''
+ raise 'Missing product name or path.'
+ else
+ download_artifact(name, path)
+ end
+end
diff --git a/scripts/github_release.rb b/scripts/github_release.rb
index 8888f8e4a77..a24dfc0a76d 100755
--- a/scripts/github_release.rb
+++ b/scripts/github_release.rb
@@ -29,17 +29,44 @@ def release_notes(version)
relevant.join.strip
end
-RELEASE_NOTES = release_notes(VERSION)
+def create_draft_release
+ name = 'Artifacts release'
+ github = Octokit::Client.new
+ github.access_token = ENV['GITHUB_ACCESS_TOKEN']
-github = Octokit::Client.new
-github.access_token = ENV['GITHUB_ACCESS_TOKEN']
+ puts 'Search for draft releases' # Previously created by a merge to master
+ releases = github.releases(REPOSITORY)
+ draft_releases = releases.select { |release| release[:name] == name && release[:draft] == true }
-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]
+ draft_releases.each { |draf_release|
+ puts 'Deleting draft release' # Previously created by a merge to master
+ response = github.delete_release(draf_release[:url])
+ }
-Dir.glob 'build/*.zip' do |upload|
- puts "Uploading #{upload} to GitHub"
- github.upload_asset(release_url, upload, content_type: 'application/zip')
+ puts 'Creating GitHub draft release'
+ response = github.create_release(REPOSITORY, RELEASE, name: name, body: "Artifacts release", draft: true)
+
+ puts "Succesfully created draft release #{response[:url]}"
+end
+
+def create_release
+ 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
+ response = github.create_release(REPOSITORY, RELEASE, name: RELEASE, body: release_notes, prerelease: false)
+ release_url = response[:url]
+
+ Dir.glob 'release_pkg/*.zip' do |upload|
+ puts "Uploading #{upload} to GitHub"
+ github.upload_asset(release_url, upload, content_type: 'application/zip')
+ end
+end
+
+if ARGV[0] == 'create-draft-release'
+ create_draft_release
+elsif ARGV[0] == 'create-release'
+ create_release
end
diff --git a/scripts/release-matrix.rb b/scripts/release-matrix.rb
new file mode 100755
index 00000000000..6e664979369
--- /dev/null
+++ b/scripts/release-matrix.rb
@@ -0,0 +1,55 @@
+#!/usr/bin/env ruby
+
+module RELEASE
+ DOCS_XCODE_VERSION = '14.3.1'
+ XCODE_VERSIONS = ['14.1', '14.2', '14.3.1', '15.0.1', '15.1']
+ PLATFORMS_NAMES = ['osx': 'macOS', 'ios': 'iOS', 'watchos': 'watchOS', 'tvos': 'tvOS', 'catalyst': 'Catalyst', 'visionos': 'visionOS']
+
+ all = ->(v) { true }
+ latest_only = ->(v) { v == XCODE_VERSIONS.last }
+ doc_version = ->(v) { v == DOCS_XCODE_VERSION }
+
+ PLATFORMS = {
+ 'osx' => all,
+ 'ios' => all,
+ 'watchos' => all,
+ 'tvos' => all,
+ 'catalyst' => all,
+ 'visionos' => latest_only,
+ }
+
+ RELEASE_XCODE_CLOUD_TARGETS = {
+ 'package-docs' => doc_version,
+ 'package' => all,
+ 'test-package-examples' => latest_only,
+ 'test-ios-static' => latest_only,
+ 'test-osx' => latest_only,
+ }
+end
+
+def get_doc_version
+ puts "#{RELEASE::DOCS_XCODE_VERSION}"
+end
+
+def plaforms_for_version(version)
+ platforms_version = []
+ RELEASE::PLATFORMS.each { |platform, filter|
+ if filter.call(version)
+ platforms_version.append(platform)
+ end
+ }
+ puts "#{platforms_version.join(",")}"
+end
+
+def get_xcode_versions
+ puts "#{RELEASE::XCODE_VERSIONS.join(",")}"
+end
+
+if ARGV[0] == 'docs_version'
+ get_doc_version
+elsif ARGV[0] == 'plaforms_for_version'
+ version = ARGV[1]
+ plaforms_for_version(version)
+elsif ARGV[0] == 'xcode_versions'
+ get_xcode_versions
+end
diff --git a/scripts/xcode_cloud_helper.rb b/scripts/xcode_cloud_helper.rb
index d4ab5af23c7..673e18c92be 100755
--- a/scripts/xcode_cloud_helper.rb
+++ b/scripts/xcode_cloud_helper.rb
@@ -5,10 +5,12 @@
require 'json'
require "base64"
require "jwt"
-require_relative "pr-ci-matrix"
require 'getoptlong'
+require_relative "pr-ci-matrix"
+require_relative "release-matrix"
include WORKFLOWS
+include RELEASE
JWT_BEARER = ''
TEAM_ID = ''
@@ -29,9 +31,11 @@ def usage()
Usage: ruby #{__FILE__} --create-workflow [name] --xcode-version [xcode_version] --token [token]
Usage: ruby #{__FILE__} --delete-workflow [workflow_id] --token [token]
Usage: ruby #{__FILE__} --build-workflow [workflow_id] --token [token]
- Usage: ruby #{__FILE__} --update-workflows --token [token]
+ Usage: ruby #{__FILE__} --create-new-workflows --token [token] --team-id [team_id]
+ Usage: ruby #{__FILE__} --create-relase-new-workflow --token [token] --team-id [team_id]
Usage: ruby #{__FILE__} --clear-unused-workflows --token [token]
Usage: ruby #{__FILE__} --get-token --issuer-id [issuer_id] --key-id [key_id] --pk_path [pk_path]
+ Usage: ruby #{__FILE__} --run-release-workflow [name] --token [token]
environment variables:
END
@@ -40,6 +44,11 @@ def usage()
APP_STORE_URL="https://api.appstoreconnect.apple.com/v1"
+def sh(*args)
+ puts "executing: #{args.join(' ')}" if false
+ system(*args, false ? {} : {:out => '/dev/null'}) || exit(1)
+end
+
def get_jwt_bearer(issuer_id, key_id, pk_path)
private_key = OpenSSL::PKey.read(File.read(pk_path))
info = {
@@ -81,6 +90,34 @@ def get_products
return list_products
end
+def get_build_actions(build_run)
+ response = get("/ciBuildRuns/#{build_run}/actions")
+ result = JSON.parse(response.body)
+ list_actions = []
+ result.collect do |doc|
+ doc[1].each { |action|
+ if action.class == Hash
+ list_actions.append({ "id" => action["id"] })
+ end
+ }
+ end
+ return list_actions
+end
+
+def get_artifacts(build_action)
+ response = get("/ciBuildActions/#{build_action}/artifacts")
+ result = JSON.parse(response.body)
+ list_artifacts = []
+ result.collect do |doc|
+ doc[1].each { |artifact|
+ if artifact.class == Hash
+ list_artifacts.append({ "id" => artifact["id"] })
+ end
+ }
+ end
+ return list_artifacts
+end
+
def get_repositories
response = get("/scmRepositories")
result = JSON.parse(response.body)
@@ -134,6 +171,16 @@ def get_workflow_info(id)
return response.body
end
+def get_build_action_info(id)
+ response = get("/ciBuildActions/#{id}")
+ return response.body
+end
+
+def get_artifact_info(id)
+ response = get("/ciArtifacts/#{id}")
+ return response.body
+end
+
def get(path)
url = "#{APP_STORE_URL}#{path}"
uri = URI.parse(url)
@@ -151,7 +198,7 @@ def get(path)
end
end
-def create_workflow(name, xcode_version)
+def create_workflow(name, xcode_version, prefix = "")
url = "#{APP_STORE_URL}/ciWorkflows"
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
@@ -159,7 +206,7 @@ def create_workflow(name, xcode_version)
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{JWT_BEARER}"
request["Content-type"] = "application/json"
- body = create_workflow_request(name, xcode_version)
+ body = create_workflow_request(name, xcode_version, prefix)
request.body = body.to_json
response = http.request(request)
if response.code == "201"
@@ -174,7 +221,7 @@ def create_workflow(name, xcode_version)
end
end
-def create_workflow_request(name, xcode_version)
+def create_workflow_request(name, xcode_version, prefix = "")
build_action = get_action_for_target(name)
pull_request_start_condition =
{
@@ -184,7 +231,7 @@ def create_workflow_request(name, xcode_version)
}
attributes =
{
- "name" => "#{name}_#{xcode_version}",
+ "name" => "#{prefix}#{name}_#{xcode_version}",
"description" => 'Create by Github Action Update XCode Cloud Workflows',
"isLockedForEditing" => false,
"containerFilePath" => "Realm.xcodeproj",
@@ -193,6 +240,7 @@ def create_workflow_request(name, xcode_version)
"pullRequestStartCondition" => pull_request_start_condition,
"actions" => build_action
}
+
xcode_version_id = get_xcode_id(xcode_version)
mac_os_id = get_macos_latest_release(xcode_version_id)
relationships =
@@ -212,7 +260,7 @@ def create_workflow_request(name, xcode_version)
return body
end
-def update_workflow(id)
+def update_workflow(id, data)
url = "#{APP_STORE_URL}/ciWorkflows/#{id}"
uri = URI.parse(url)
http = Net::HTTP.new(uri.host, uri.port)
@@ -220,16 +268,10 @@ def update_workflow(id)
request = Net::HTTP::Patch.new(uri)
request["Authorization"] = "Bearer #{JWT_BEARER}"
request["Content-type"] = "application/json"
- data =
- {
- "type" => "ciWorkflows",
- "attributes" => { "isEnabled" => true },
- "id" => id
- }
body = { "data" => data }
request.body = body.to_json
response = http.request(request)
- if response.code == "201"
+ if response.code == "200"
result = JSON.parse(response.body)
id = result["data"]["id"]
puts "Worfklow updated #{id}"
@@ -266,7 +308,7 @@ def start_build(id)
data =
{
"type" => "ciBuildRuns",
- "attributes" => {},
+ "attributes" => { "clean" => true },
"relationships" => { "workflow" => { "data" => { "type" => "ciWorkflows", "id" => id }}}
}
body = { "data" => data }
@@ -274,10 +316,11 @@ def start_build(id)
response = http.request(request)
if response.code == "201"
result = JSON.parse(response.body)
- id = result["data"]["id"]
+ build_id = result["data"]["id"]
puts "Workflow build started with id: #{id}:"
+ puts "Running build https://appstoreconnect.apple.com/teams/#{TEAM_ID}/frameworks/#{get_realm_product_id}/builds/#{build_id}/"
puts response.body
- return id
+ return build_id
else
raise "Error: #{response.code} #{response.body}"
end
@@ -349,6 +392,49 @@ def create_new_workflows
end
end
+def create_new_release_workflows
+ if !ENV.include?('CI')
+ print 'Are you sure you want to create this workflows?, this will create declared local workflows that may not currently working in other PRs [Y/N]\n'
+ user_input = STDIN.gets.chomp.downcase
+ else
+ user_input = 'y'
+ end
+
+ if user_input == "y"
+ current_workflows = get_workflows()
+ .filter { |workflow|
+ workflow["attributes"]["name"].split('_').first == 'release'
+ }
+ .map { |workflow|
+ target = workflow["attributes"]["name"].split('_')
+ name = target[1]
+ version = target.last
+ { 'target' => name, 'version' => version }
+ }
+
+ workflows_to_create = []
+ RELEASE::RELEASE_XCODE_CLOUD_TARGETS.each { |name, filter|
+ RELEASE::XCODE_VERSIONS.each { |version|
+ if filter.call(version)
+ workflow = { "target" => name, "version" => version }
+ unless current_workflows.include? workflow
+ workflows_to_create.append(workflow)
+ end
+ end
+ }
+ }
+
+ workflows_to_create.each { |workflow|
+ name = workflow['target']
+ version = workflow['version']
+ puts "Creating new workflow for target: #{name} and version #{version} for release"
+ workflow_id = create_workflow(name, version, 'release')
+ }
+ else
+ puts "No"
+ end
+end
+
def delete_unused_workflows
if !ENV.include?('CI')
print "Are you sure you want to clear unused workflow?, this will delete not-declared local workflows that may be currently working in other PRs [Y/N]\n"
@@ -369,7 +455,7 @@ def delete_unused_workflows
remote_workflows = get_workflows
remote_workflows.each.map { |workflow|
- if workflow["attributes"]["name"].include? "Cocoa-prepare"
+ if workflow["attributes"]["name"].include? "release"
return nil
end
@@ -384,6 +470,41 @@ def delete_unused_workflows
end
end
+def delete_unused_release_workflows
+ if !ENV.include?('CI')
+ print "Are you sure you want to clear unused workflow?, this will delete not-declared local workflows that may be currently working in other PRs [Y/N]\n"
+ user_input = STDIN.gets.chomp.downcase
+ else
+ user_input = 'y'
+ end
+
+ if user_input == "y"
+ local_workflows = ["release_package-docs_#{RELEASE::DOCS_XCODE_VERSION}"]
+ RELEASE::RELEASE_XCODE_CLOUD_TARGETS.each { |name, filter|
+ RELEASE::XCODE_VERSIONS.each { |version|
+ if filter.call(version)
+ local_workflows.append("release_#{name}_#{version}")
+ end
+ }
+ }
+
+ remote_workflows = get_workflows
+ .filter { |workflow|
+ workflow["attributes"]["name"].split('_').first == 'release'
+ }
+ .map { |workflow|
+ name = workflow["attributes"]["name"]
+ unless local_workflows.include? name
+ puts "Deleting unused release workflow #{workflow["id"]} #{name}"
+ delete_workflow(workflow["id"])
+ end
+ }
+
+ else
+ puts "No"
+ end
+end
+
def get_action_for_target(name)
workflow_id = get_workflow_id_for_name(name)
if workflow_id.nil?
@@ -493,8 +614,9 @@ def get_xcode_id(version)
list_xcodeversion = get_xcode_versions
$xcode_list = list_xcodeversion
end
+
list_xcodeversion.each do |xcode|
- if xcode["name"] == "Xcode #{version}"
+ if xcode["name"].include? "#{version}"
return xcode["id"]
end
end
@@ -550,13 +672,78 @@ def get_workflow_id_for_name(name)
$workflows_list = workflows
end
workflows.each do |workflow|
- if workflow["attributes"]["name"].split('_')[0] == name
+ if workflow["attributes"]["name"] == name
return workflow["id"]
end
end
return nil
end
+def read_build_info(build_id)
+ response = get("/ciBuildRuns/#{build_id}")
+ return response.body
+end
+
+def run_release_workflow(name)
+ puts "Running workflow #{name}"
+ workflow_id = get_workflow_id_for_name(name)
+ build_id = start_build(workflow_id)
+end
+
+def check_status_and_wait(build_run)
+ begin
+ build_state = read_build_info(build_run)
+ result = JSON.parse(build_state)
+ status = result["data"]["attributes"]["executionProgress"]
+ puts "Current status #{status}"
+ puts 'Waiting'
+ if status == 'COMPLETE'
+ completed = true
+ end
+ end until completed == true or not sleep 20
+
+ build_state = read_build_info(build_run)
+ result = JSON.parse(build_state)
+ completion_status = result["data"]["attributes"]["completionStatus"]
+ #if completion_status != 'SUCCEEDED'
+ # puts "Completion status #{completion_status}"
+ # raise "Error running build"
+ #end
+ get_logs_for_build(build_run)
+ return
+end
+
+def get_logs_for_build(build_run)
+ actions = get_build_actions(build_run)
+ artifacts = get_artifacts(actions[0]["id"])
+ artifact_info = get_artifact_info(artifacts[0]["id"])
+ result = JSON.parse(artifact_info)
+ artifact_url = result["data"]["attributes"]["downloadUrl"]
+ print_artifact_logs(artifact_url)
+end
+
+def check_workflow_execution_status(build_id)
+ build_state = read_build_info(build_id)
+ result = JSON.parse(build_state)
+ status = result["data"]["attributes"]["executionProgress"]
+ return status
+end
+
+def create_workflow_and_run(name, xcode_version, prefix)
+ workflow_id = create_workflow(name, xcode_version, prefix)
+ build_run = start_build(workflow_id)
+ check_status_and_wait(build_run)
+end
+
+def print_artifact_logs(url)
+ sh 'curl', '--output', 'logs.zip', "#{url}"
+ sh 'unzip', "logs.zip"
+ file_name = Dir["RealmSwift*/ci_post_clone.log"]
+ text = File.readlines("#{file_name[0]}").map do |line|
+ puts line
+ end
+end
+
opts = GetoptLong.new(
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
[ '--token', '-t', GetoptLong::REQUIRED_ARGUMENT ],
@@ -565,6 +752,7 @@ def get_workflow_id_for_name(name)
[ '--issuer-id', GetoptLong::REQUIRED_ARGUMENT ],
[ '--key-id', GetoptLong::REQUIRED_ARGUMENT ],
[ '--pk-path', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--prefix', GetoptLong::REQUIRED_ARGUMENT ],
[ '--list-workflows', GetoptLong::NO_ARGUMENT ],
[ '--list-products', GetoptLong::NO_ARGUMENT ],
[ '--list-repositories', GetoptLong::NO_ARGUMENT ],
@@ -576,17 +764,25 @@ def get_workflow_id_for_name(name)
[ '--delete-workflow', GetoptLong::REQUIRED_ARGUMENT ],
[ '--build-workflow', GetoptLong::REQUIRED_ARGUMENT ],
[ '--create-new-workflows', GetoptLong::NO_ARGUMENT ],
+ [ '--create-new-release-workflows', GetoptLong::NO_ARGUMENT ],
[ '--clear-unused-workflows', GetoptLong::NO_ARGUMENT ],
+ [ '--clear-unused-release-workflows', GetoptLong::NO_ARGUMENT ],
+ [ '--run-release-workflow', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--check-workflow-status', GetoptLong::REQUIRED_ARGUMENT ],
+ [ '--create-release-workflow-and-run', GetoptLong::REQUIRED_ARGUMENT ],
[ '--get-token', GetoptLong::NO_ARGUMENT ]
)
option = ''
name = ''
workflow_id = ''
+build_id = ''
xcode_version = ''
issuer_id = ''
key_id = ''
pk_path = ''
+release_workflow_name = ''
+prefix = ''
opts.each do |opt, arg|
if opt != '--token' && opt != '--xcode-version' && opt != '--issuer-id' && opt != '--key-id' && opt != '--pk-path' && opt != '--team-id'
@@ -618,6 +814,9 @@ def get_workflow_id_for_name(name)
--pk-path [pk_path]:
Apple Connect API path to private key file.
+--prefix [prefix]:
+ Prefix name for a new workflow.
+
--list-workflows:
Returns a list of current workflows for the RealmSwift product.
@@ -651,10 +850,25 @@ def get_workflow_id_for_name(name)
--create-new-workflows:
Adds the missing workflows corresponding to the list of targets and xcode versions in `pr-ci-matrix.rb`.
-
+
+--create-new-release-workflows
+ Create new workflows for the release pipeline.
+
--clear-unused-workflows:
Clear all unused workflows which are not in the list of targets and xcode versions in `pr-ci-matrix.rb`.
+--clear-unused-release-workflows
+ Clear all unused workflows for the release pipeline
+
+--run-release-workflow
+ Runs a release workflow
+
+--check-workflow-status
+ Check workflow status
+
+--create-release-workflow-and-run
+ Creates a workflow, runs it and wait for it to finish
+
--get-token:
Get Apple Connect Store API Token for local use.
@@ -682,11 +896,15 @@ def get_workflow_id_for_name(name)
if arg != ''
pk_path = arg
end
+ when '--prefix'
+ if arg != ''
+ prefix = arg
+ end
when '--info-workflow'
if arg != ''
workflow_id = arg
end
- when '--create-workflow'
+ when '--create-workflow', '--create-release-workflow-and-run'
if arg != ''
name = arg
end
@@ -698,6 +916,14 @@ def get_workflow_id_for_name(name)
if arg != ''
xcode_version = arg
end
+ when '--run-release-workflow'
+ if arg != ''
+ release_workflow_name = arg
+ end
+ when '--check-workflow-status'
+ if arg != ''
+ build_id = arg
+ end
end
end
@@ -725,7 +951,7 @@ def get_workflow_id_for_name(name)
if name == '' || xcode_version == ''
raise 'Needs name and xcode version'
else
- create_workflow(name, xcode_version)
+ create_workflow(name, xcode_version, prefix)
end
elsif option == '--update-workflow'
if workflow_id == ''
@@ -751,12 +977,38 @@ def get_workflow_id_for_name(name)
else
create_new_workflows
end
+elsif option == '--create-new-release-workflows'
+ if TEAM_ID == ''
+ raise 'Needs team id'
+ else
+ create_new_release_workflows
+ end
elsif option == '--clear-unused-workflows'
delete_unused_workflows
+elsif option == '--clear-unused-release-workflows'
+ delete_unused_release_workflows
elsif option == '--get-token'
if issuer_id == '' || key_id == '' || pk_path == ''
raise 'Needs issuer id, key id or pk id.'
else
get_jwt_bearer(issuer_id, key_id, pk_path)
end
+elsif option == '--run-release-workflow'
+ if release_workflow_name == ''
+ raise 'Needs workflow name to run.'
+ else
+ run_release_workflow(release_workflow_name)
+ end
+elsif option == '--check-workflow-status'
+ if build_id == ''
+ raise 'Needs build id name to run.'
+ else
+ check_workflow_execution_status(build_id)
+ end
+elsif option == '--create-release-workflow-and-run'
+ if name == '' || xcode_version == ''
+ raise 'Needs name and xcode version'
+ else
+ create_workflow_and_run(name, xcode_version, "release_")
+ end
end