diff --git a/.github/ISSUE_TEMPLATE/BUG-REPORT.yml b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml new file mode 100644 index 000000000..07eff39ab --- /dev/null +++ b/.github/ISSUE_TEMPLATE/BUG-REPORT.yml @@ -0,0 +1,88 @@ +name: 🐞 Bug +description: File a bug/issue +title: "[BUG] " +labels: ["bug", "needs-triage"] +body: +- type: checkboxes + attributes: + label: Is there an existing issue for this? + description: Please search to see if an issue already exists for the bug you encountered. + options: + - label: I have searched the existing issues + required: true +- type: textarea + attributes: + label: SDK Version + description: Version of the SDK in use? + validations: + required: true +- type: textarea + attributes: + label: iOS Version + description: Version of iOS in use? + validations: + required: true +- type: textarea + attributes: + label: Current Behavior + description: A concise description of what you're experiencing. + validations: + required: true +- type: textarea + attributes: + label: Expected Behavior + description: A concise description of what you expected to happen. + validations: + required: true +- type: textarea + attributes: + label: Steps To Reproduce + description: Steps to reproduce the behavior. + placeholder: | + 1. In this environment... + 1. With this config... + 1. Run '...' + 1. See error... + validations: + required: true +- type: textarea + attributes: + label: Link + description: Link to code demonstrating the problem. + validations: + required: false +- type: textarea + attributes: + label: Logs / Stacktraces + description: Logs/stack traces related to the problem (⚠️do not include sensitive information). + validations: + required: false +- type: dropdown + attributes: + label: Severity + description: What is the severity of the problem? + multiple: true + options: + - Blocking development + - Affecting users + - Minor issue + validations: + required: false +- type: textarea + attributes: + label: Workaround/Solution + description: Do you have any workaround or solution in mind for the problem? + validations: + required: false +- type: textarea + attributes: + label: "Recent Change" + description: Has this issue started happening after an update or experiment change? + validations: + required: false +- type: textarea + attributes: + label: Conflicts + description: Are there other libraries/dependencies potentially in conflict? + validations: + required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/ENHANCEMENT.yml b/.github/ISSUE_TEMPLATE/ENHANCEMENT.yml new file mode 100644 index 000000000..79c53247b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/ENHANCEMENT.yml @@ -0,0 +1,45 @@ +name: ✨Enhancement +description: Create a new ticket for a Enhancement/Tech-initiative for the benefit of the SDK which would be considered for a minor version update. +title: "[ENHANCEMENT] <title>" +labels: ["enhancement"] +body: + - type: textarea + id: description + attributes: + label: "Description" + description: Briefly describe the enhancement in a few sentences. + placeholder: Short description... + validations: + required: true + - type: textarea + id: benefits + attributes: + label: "Benefits" + description: How would the enhancement benefit to your product or usage? + placeholder: Benefits... + validations: + required: true + - type: textarea + id: detail + attributes: + label: "Detail" + description: How would you like the enhancement to work? Please provide as much detail as possible + placeholder: Detailed description... + validations: + required: false + - type: textarea + id: examples + attributes: + label: "Examples" + description: Are there any examples of this enhancement in other products/services? If so, please provide links or references. + placeholder: Links/References... + validations: + required: false + - type: textarea + id: risks + attributes: + label: "Risks/Downsides" + description: Do you think this enhancement could have any potential downsides or risks? + placeholder: Risks/Downsides... + validations: + required: false \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.md b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.md new file mode 100644 index 000000000..a061f3356 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/FEATURE-REQUEST.md @@ -0,0 +1,4 @@ +<!-- + Thanks for filing in issue! Are you requesting a new feature? If so, please share your feedback with us on the following link. +--> +## Feedback requesting a new feature can be shared [here.](https://feedback.optimizely.com/) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..d28ef3dd4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: 💡Feature Requests + url: https://feedback.optimizely.com/ + about: Feedback requesting a new feature can be shared here. \ No newline at end of file diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml new file mode 100644 index 000000000..181fcbf4e --- /dev/null +++ b/.github/workflows/integration_tests.yml @@ -0,0 +1,50 @@ +name: Reusable action of Integration tests + +on: + workflow_call: + secrets: + CI_USER_TOKEN: + required: true + TRAVIS_COM_TOKEN: + required: true + +jobs: + integration_tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + # You should create a personal access token and store it in your repository + token: ${{ secrets.CI_USER_TOKEN }} + repository: 'optimizely/travisci-tools' + path: 'home/runner/travisci-tools' + ref: 'master' + - name: set SDK Branch if PR + if: ${{ github.event_name == 'pull_request' }} + run: | + echo "SDK_BRANCH=${{ github.head_ref }}" >> $GITHUB_ENV + - name: set SDK Branch if not pull request + if: ${{ github.event_name != 'pull_request' }} + run: | + echo "SDK_BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV + echo "TRAVIS_BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV + - name: Trigger build + env: + SDK: swift + TESTAPP_TAG: master + BUILD_NUMBER: ${{ github.run_id }} + TESTAPP_BRANCH: master + GITHUB_TOKEN: ${{ secrets.CI_USER_TOKEN }} + EVENT_TYPE: ${{ github.event_name }} + GITHUB_CONTEXT: ${{ toJson(github) }} + #REPO_SLUG: ${{ github.repository }} + PULL_REQUEST_SLUG: ${{ github.repository }} + UPSTREAM_REPO: ${{ github.repository }} + PULL_REQUEST_SHA: ${{ github.event.pull_request.head.sha }} + PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + UPSTREAM_SHA: ${{ github.sha }} + TOKEN: ${{ secrets.TRAVIS_COM_TOKEN }} + EVENT_MESSAGE: ${{ github.event.message }} + HOME: 'home/runner' + run: | + home/runner/travisci-tools/trigger-script-with-status-update.sh diff --git a/.github/workflows/lint_markdown.yml b/.github/workflows/lint_markdown.yml new file mode 100644 index 000000000..e3e903143 --- /dev/null +++ b/.github/workflows/lint_markdown.yml @@ -0,0 +1,19 @@ +name: Reusable action of linting markdown files + +on: [workflow_call] + +jobs: + lint_markdown: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.6' + bundler-cache: true # runs 'bundle install' and caches installed gems automatically + - name: Install gem and Run tests + run: | + cd ../../ + gem install awesome_bot + find . -type f -name '*.md' -exec awesome_bot {} \; diff --git a/.github/workflows/source_clear_cron.yml b/.github/workflows/source_clear_cron.yml new file mode 100644 index 000000000..ca1a715b8 --- /dev/null +++ b/.github/workflows/source_clear_cron.yml @@ -0,0 +1,20 @@ +name: Source clear + +on: + push: + branches: [ master ] + schedule: + # Runs "weekly" + - cron: '0 0 * * 0' + +jobs: + source_clear: + runs-on: macos-12 + steps: + - uses: actions/checkout@v3 + - name: Source clear scan + env: + SRCCLR_API_TOKEN: ${{ secrets.SRCCLR_API_TOKEN }} + run: | + gem install cocoapods -v '1.9.3' + curl -sSL https://download.sourceclear.com/ci.sh | bash -s - scan diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml new file mode 100644 index 000000000..086a9832c --- /dev/null +++ b/.github/workflows/swift.yml @@ -0,0 +1,116 @@ +name: Swift + +on: + push: + branches: [ 'master' ] + pull_request: + branches: [ '*' ] + workflow_dispatch: + inputs: + PREP: + required: false + type: boolean + description: prepare to release + RELEASE: + required: false + type: boolean + description: release + +env: + VERSION: 3.10.5 + +jobs: + + lint_markdown_files: + uses: optimizely/swift-sdk/.github/workflows/lint_markdown.yml@master + + integration_tests: + if: "${{ github.event.inputs.PREP == '' && github.event.inputs.RELEASE == '' }}" + uses: optimizely/swift-sdk/.github/workflows/integration_tests.yml@master + secrets: + CI_USER_TOKEN: ${{ secrets.CI_USER_TOKEN }} + TRAVIS_COM_TOKEN: ${{ secrets.TRAVIS_COM_TOKEN }} + + lint: + runs-on: macos-12 + steps: + - uses: actions/checkout@v3 + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: 14.1.0 + - env: + SRCCLR_API_TOKEN: ${{ secrets.SRCCLR_API_TOKEN }} + run: | + gem install cocoapods -v '1.9.3' + pod spec lint --quick + curl -sSL https://download.sourceclear.com/ci.sh | bash + + unittests: + if: "${{ github.event.inputs.PREP == '' && github.event.inputs.RELEASE == '' }}" + uses: optimizely/swift-sdk/.github/workflows/unit_tests.yml@master + + prepare_for_release: + runs-on: macos-12 + if: "${{ github.event.inputs.PREP == 'true' && github.event_name == 'workflow_dispatch' }}" + steps: + - uses: actions/checkout@v3 + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: 14.1.0 + - name: Install Homebrew + run: | + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + + - name: Install Hub + run: | + brew install hub + + - name: Verify Hub Installation + run: | + hub version + - id: prepare_for_release + name: Prepare for release + env: + HOME: 'home/runner' + REPO_SLUG: ${{ github.repository }} + BRANCH: ${{ github.ref_name }} + GITHUB_USER: optibot + GITHUB_TOKEN: ${{ secrets.CI_USER_TOKEN }} + COCOAPODS_VERSION: '1.12.1' + run: | + gem install cocoapods -v $COCOAPODS_VERSION + Scripts/run_prep.sh + - name: Check prepare for release failure + if: steps.prepare_for_release.conclusion == 'failure' + run: cat /tmp/build.out + + release: + if: "${{github.event.inputs.RELEASE == 'true' && github.event_name == 'workflow_dispatch' }}" + runs-on: macos-12 + steps: + - uses: actions/checkout@v3 + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: 14.1.0 + - name: Install Homebrew + run: | + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + + - name: Install Hub + run: | + brew install hub + + - name: Verify Hub Installation + run: | + hub version + - name: Push to cocoapods.org + env: + HOME: 'home/runner' + REPO_SLUG: ${{ github.repository }} + BRANCH: ${{ github.ref_name }} + GITHUB_TOKEN: ${{ secrets.CI_USER_TOKEN }} + COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }} + COCOAPODS_VERSION: '1.12.1' + run: | + gem install cocoapods -v $COCOAPODS_VERSION + Scripts/run_release.sh diff --git a/.github/workflows/ticket_reference_check.yml b/.github/workflows/ticket_reference_check.yml new file mode 100644 index 000000000..b7d52780f --- /dev/null +++ b/.github/workflows/ticket_reference_check.yml @@ -0,0 +1,16 @@ +name: Jira ticket reference check + +on: + pull_request: + types: [opened, edited, reopened, synchronize] + +jobs: + + jira_ticket_reference_check: + runs-on: ubuntu-latest + + steps: + - name: Check for Jira ticket reference + uses: optimizely/github-action-ticket-reference-checker-public@master + with: + bodyRegex: 'FSSDK-(?<ticketNumber>\d+)' diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml new file mode 100644 index 000000000..3fd6ddfdd --- /dev/null +++ b/.github/workflows/unit_tests.yml @@ -0,0 +1,113 @@ +name: Reusable action of Unit tests + +on: [workflow_call] + +env: + COVERAGE_DIR: ./COVERAGE + +jobs: + unittests: + runs-on: macos-12 + strategy: + fail-fast: false + matrix: + # github actions are very poor in supporting iOS simulators: + # - multiple Xcode versions are pre installed (not all versions and they change supported xcode versions frequently) + # - each xcode version has own simulator os versions. + # - so to run tests with the target simulator, we have to find a proper xcode version pre-installed and support the target simulator os version. + # also, the xcode version (simulator_xcode_version) and simulator os versions (os) are moving target. We have to change these time to time. + # - see "https://github.com/actions/runner-images/blob/main/images/macos/macos-11-Readme.md" for installed macOS, xcode and simulator versions. + include: + - os: 16.1 + device: "iPhone 12" + scheme: "OptimizelySwiftSDK-iOS" + test_sdk: "iphonesimulator" + platform: "iOS Simulator" + os_type: "iOS" + simulator_xcode_version: 14.1 + - os: 15.5 + device: "iPhone 12" + scheme: "OptimizelySwiftSDK-iOS" + test_sdk: "iphonesimulator" + platform: "iOS Simulator" + os_type: "iOS" + simulator_xcode_version: 13.4.1 + - os: 15.5 + # good to have tests with older OS versions, but it looks like this is min OS+xcode versions supported by github actions + device: "iPad Air (4th generation)" + scheme: "OptimizelySwiftSDK-iOS" + test_sdk: "iphonesimulator" + platform: "iOS Simulator" + os_type: "iOS" + simulator_xcode_version: 13.4.1 + - os: 16.1 + device: "Apple TV" + scheme: "OptimizelySwiftSDK-tvOS" + test_sdk: "appletvsimulator" + platform: "tvOS Simulator" + os_type: "tvOS" + simulator_xcode_version: 14.1 + steps: + - uses: actions/checkout@v3 + - uses: maxim-lobanov/setup-xcode@v1 + with: + # macos version and supported simulator_xcode_versions are all related to this xcode_version, so be careful when you upgrade this. + xcode-version: 14.1 + - name: set SDK Branch if PR + if: ${{ github.event_name == 'pull_request' }} + run: | + echo "BRANCH=${{ github.base_ref }}" >> $GITHUB_ENV + - name: set SDK Branch if not pull request + if: ${{ github.event_name != 'pull_request' }} + run: | + echo "BRANCH=${{ github.ref_name }}" >> $GITHUB_ENV + - id: unit_tests + env: + SCHEME: ${{ matrix.scheme }} + TEST_SDK: ${{ matrix.test_sdk }} + PLATFORM: ${{ matrix.platform }} + OS: ${{ matrix.os }} + OS_TYPE: ${{ matrix.os_type }} + SIMULATOR_XCODE_VERSION: ${{ matrix.simulator_xcode_version }} + NAME: ${{ matrix.device }} + run: | + gem install coveralls-lcov + gem install cocoapods -v '1.11.3' + pod repo update + pod install + HOMEBREW_NO_INSTALL_CLEANUP=true brew update && brew install jq + + # github actions are very poor in supporting iOS simulators: + # - to find pre-installed xcode version, run this: + ##ls /Applications/ + # - to find supported simulator os versions, run this (and find simulator with non-error "datapath") + ##xcrun simctl list --json devices + + # switch to the target xcode version + sudo xcode-select -switch /Applications/Xcode_$SIMULATOR_XCODE_VERSION.app + + Scripts/prepare_simulator.sh + Scripts/run_unit_tests.sh + - name: Check on failures (Archive Test Results) + uses: actions/upload-artifact@v3 + if: steps.unit_tests.outcome != 'success' + with: + name: build-logs-${{ matrix.device }}-${{ matrix.os }} + path: build/Logs + - # - report coverage for PR and iPhone 11 only (avoid redundant ones) + # - use Xcode12.4+ (older Xcode reports a wrong number) + name: Check on success + id: coveralls + if: ${{ steps.unit_tests.outcome == 'success' && env.BRANCH == 'master' && env.PLATFORM == 'iOS Simulator' && env.NAME == 'iPhone 11' }} + env: + PLATFORM: ${{ matrix.platform }} + NAME: ${{ matrix.device }} + run: | + Scripts/prepare_coveralls_report.sh + sleep 5 + - name: Upload coveralls report + if: steps.coveralls.outcome == 'success' + uses: coverallsapp/github-action@master + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + path-to-lcov: ./xccov2lcov/lcov.info diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 83c87e49a..000000000 --- a/.travis.yml +++ /dev/null @@ -1,136 +0,0 @@ -language: minimal -os: linux - -# Integration tests need to run first to reset the PR build status to pending -stages: - - name: 'Source Clear' - - name: 'Lint markdown files' - - name: 'Trigger Integration Tests' - if: env(RUN_COMPAT_SUITE) = true and env(PREP) IS NOT present and env(RELEASE) IS NOT present - - name: 'Lint' - - name: 'Unit Tests' - if: env(PREP) IS NOT present and env(RELEASE) IS NOT present - - name: 'Prepare for release' - if: env(PREP) = true and type = api - - name: 'Release' - if: env(RELEASE) = true and type = api - -jobs: - include: - - stage: 'Lint markdown files' - os: linux - language: generic - install: gem install awesome_bot - script: - - find . -type f -name '*.md' -exec awesome_bot {} \; - notifications: - email: false - - - stage: 'Trigger Integration Tests' - language: minimal - os: linux - env: - - SDK=swift - - BUILD_NUMBER=${TRAVIS_JOB_NUMBER/.} - - TESTAPP_TAG=master - - SDK_BRANCH=$TRAVIS_PULL_REQUEST_BRANCH - - cache: false - install: - - mkdir $HOME/travisci-tools && pushd $HOME/travisci-tools && git init && git pull https://$CI_USER_TOKEN@github.com/optimizely/travisci-tools.git && popd - script: - - $HOME/travisci-tools/trigger-script-with-status-update.sh - - - stage: 'Lint' - language: swift - os: osx - osx_image: xcode12.4 - install: - - gem install cocoapods -v '1.9.3' - script: - - pod spec lint --quick - after_script: - - curl -sSL https://download.sourceclear.com/ci.sh | bash - - - stage: 'Source Clear' - if: type = cron - addons: - srcclr: true - before_install: skip - install: skip - before_script: skip - script: skip - after_success: skip - - - &unittests - stage: 'Unit Tests' - language: swift - os: osx - osx_image: xcode12.4 - branches: - only: - - master - env: COVERAGE_DIR=./COVERAGE SCHEME=OptimizelySwiftSDK-iOS TEST_SDK=iphonesimulator PLATFORM='iOS Simulator' OS=14.4 NAME='iPhone 11' - name: PLATFORM='iOS Simulator' OS=14.4 NAME='iPhone 11' - install: - #- gem install slather --no-document --quiet - - gem install coveralls-lcov - # - - gem install cocoapods -v '1.9.3' - - pod repo update - - pod install - # install jq without cleaning up - - HOMEBREW_NO_INSTALL_CLEANUP=true brew update && brew install jq - # preload simulator - - Scripts/start_simulator.sh - script: - - Scripts/run_unit_tests.sh - after_success: - - Scripts/upload_coveralls.sh - - sleep 5 # https://github.com/travis-ci/travis-ci/issues/4725 - after_failure: - # install travis artifacts uploader - - sudo curl -sL https://raw.githubusercontent.com/travis-ci/artifacts/master/install | bash - - artifacts upload --target-paths "/${TRAVIS_REPO_SLUG}/${TRAVIS_BUILD_NUMBER}/${TRAVIS_JOB_NUMBER}/xcodebuild_logs" $(find /Users/travis/Library/Developer/Xcode/ -name *.xcresult -o -name *.log) - - artifacts upload --target-paths "/${TRAVIS_REPO_SLUG}/${TRAVIS_BUILD_NUMBER}/${TRAVIS_JOB_NUMBER}/buildoutput" "$TRAVIS_BUILD_DIR/buildoutput" - - <<: *unittests - env: COVERAGE_DIR=./COVERAGE SCHEME=OptimizelySwiftSDK-iOS TEST_SDK=iphonesimulator PLATFORM='iOS Simulator' OS=13.3 NAME='iPhone 8' - name: PLATFORM='iOS Simulator' OS=13.3 NAME='iPhone 8' - - <<: *unittests - env: COVERAGE_DIR=./COVERAGE SCHEME=OptimizelySwiftSDK-iOS TEST_SDK=iphonesimulator PLATFORM='iOS Simulator' OS=11.4 NAME='iPad Air' - name: PLATFORM='iOS Simulator' OS=11.4 NAME='iPad Air' - - <<: *unittests - env: COVERAGE_DIR=./COVERAGE SCHEME=OptimizelySwiftSDK-tvOS TEST_SDK=appletvsimulator PLATFORM='tvOS Simulator' OS=12.1 NAME='Apple TV 4K' - name: PLATFORM='tvOS Simulator' OS=12.1 NAME='Apple TV 4K' - - - stage: 'Prepare for release' - name: Prepare for release - language: swift - os: osx - osx_image: xcode12.4 - env: - - VERSION=3.10.1 - install: - # install hub - - wget https://github.com/github/hub/releases/download/v2.11.2/hub-darwin-amd64-2.11.2.tgz -O /tmp/hub-darwin-amd64-2.11.2.tgz && tar -xvf /tmp/hub-darwin-amd64-2.11.2.tgz -C /usr/local/opt && ln -s /usr/local/opt/hub-darwin-amd64-2.11.2/bin/hub /usr/local/bin/hub - # upgrade cocoapods - - gem install cocoapods -v '1.9.3' - script: - - Scripts/run_prep.sh - after_failure: - - cat /tmp/build.out - - - stage: 'Release' - name: Push to cocoapods.org - language: minimal - os: osx - osx_image: xcode12.4 - env: - - VERSION=3.10.1 - install: - # install hub - - wget https://github.com/github/hub/releases/download/v2.11.2/hub-darwin-amd64-2.11.2.tgz -O /tmp/hub-darwin-amd64-2.11.2.tgz && tar -xvf /tmp/hub-darwin-amd64-2.11.2.tgz -C /usr/local/opt && ln -s /usr/local/opt/hub-darwin-amd64-2.11.2/bin/hub /usr/local/bin/hub - # upgrade cocoapods - - gem install cocoapods -v '1.9.3' - script: - - Scripts/run_release.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a0417dca..f6038dd22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ # Optimizely Swift SDK Changelog +## 3.10.5 +Jan 19, 2024 + +### Bug Fixes +* Value for required reason API fixed at privacy manifest file. ([#541](https://github.com/optimizely/swift-sdk/pull/541)) +* Add coccoapods support to bundle privacy manifest file. ([#542](https://github.com/optimizely/swift-sdk/pull/542)) +* Add SPM support to bundle privacy manifest file. ([#544](https://github.com/optimizely/swift-sdk/pull/544)) + +## 3.10.4 +December 8, 2023 + +### Enhancement +* Add privacy manifest file ([#522](https://github.com/optimizely/swift-sdk/pull/522/)). + +## 3.10.3 +December 7, 2023 + +### Bug Fixes +* Remove redundant post request body in upload task. ([#521](https://github.com/optimizely/swift-sdk/pull/521/)). + +### Enhancement +* Handle duplicate keys in experiment ([#523](https://github.com/optimizely/swift-sdk/pull/523/)). + +## 3.10.2 +March 14, 2023 + +* We updated our README.md and other non-functional code to reflect that this SDK supports both Optimizely Feature Experimentation and Optimizely Full Stack. ([#479](https://github.com/optimizely/swift-sdk/pull/479)). + ## 3.10.1 April 8, 2022 diff --git a/OptimizelySwiftSDK.podspec b/OptimizelySwiftSDK.podspec index da440c498..e0b08a57b 100644 --- a/OptimizelySwiftSDK.podspec +++ b/OptimizelySwiftSDK.podspec @@ -1,9 +1,9 @@ Pod::Spec.new do |s| s.name = "OptimizelySwiftSDK" s.module_name = "Optimizely" - s.version = "3.10.1" + s.version = "3.10.5" s.summary = "Optimizely experiment framework for iOS/tvOS/watchOS" - s.homepage = "https://docs.developers.optimizely.com/full-stack/docs" + s.homepage = "https://docs.developers.optimizely.com/experimentation/v4.0.0-full-stack/docs" s.license = { :type => "Apache License, Version 2.0", :file => "LICENSE" } s.author = { "Optimizely" => "support@optimizely.com" } s.ios.deployment_target = "10.0" @@ -15,6 +15,7 @@ Pod::Spec.new do |s| :tag => "v"+s.version.to_s } s.source_files = "Sources/**/*.swift" + s.resource_bundles = { 'OptimizelySwiftSDK' => ['Sources/Supporting Files/PrivacyInfo.xcprivacy'] } s.swift_version = ["5.0", "5.1"] s.framework = "Foundation" s.requires_arc = true diff --git a/OptimizelySwiftSDK.xcodeproj/project.pbxproj b/OptimizelySwiftSDK.xcodeproj/project.pbxproj index ceb2e999a..3267c2e1d 100644 --- a/OptimizelySwiftSDK.xcodeproj/project.pbxproj +++ b/OptimizelySwiftSDK.xcodeproj/project.pbxproj @@ -1739,6 +1739,10 @@ 84E7ABC827D2A1F100447CAE /* ThreadSafeLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E7ABBA27D2A1F100447CAE /* ThreadSafeLogger.swift */; }; 84E7ABC927D2A1F100447CAE /* ThreadSafeLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E7ABBA27D2A1F100447CAE /* ThreadSafeLogger.swift */; }; 84E7ABCA27D2A1F100447CAE /* ThreadSafeLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E7ABBA27D2A1F100447CAE /* ThreadSafeLogger.swift */; }; + 98D47DAB2B224627001D38AC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 98D47DAA2B224627001D38AC /* PrivacyInfo.xcprivacy */; }; + 98D47DAC2B224627001D38AC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 98D47DAA2B224627001D38AC /* PrivacyInfo.xcprivacy */; }; + 98D47DAD2B224627001D38AC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 98D47DAA2B224627001D38AC /* PrivacyInfo.xcprivacy */; }; + 98D47DAE2B224627001D38AC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 98D47DAA2B224627001D38AC /* PrivacyInfo.xcprivacy */; }; BD1C3E8524E4399C0084B4DA /* SemanticVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B97DD93249D327F003DE606 /* SemanticVersion.swift */; }; BD64853C2491474500F30986 /* Optimizely.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E75167A22C520D400B2B157 /* Optimizely.h */; settings = {ATTRIBUTES = (Public, ); }; }; BD64853E2491474500F30986 /* Audience.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E75169822C520D400B2B157 /* Audience.swift */; }; @@ -2149,6 +2153,7 @@ 75C719BB25E4519B0084187E /* Optimizely.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Optimizely.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 75C71C3825E45A2B0084187E /* WatchBackgroundNotifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchBackgroundNotifier.swift; sourceTree = "<group>"; }; 84E7ABBA27D2A1F100447CAE /* ThreadSafeLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadSafeLogger.swift; sourceTree = "<group>"; }; + 98D47DAA2B224627001D38AC /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; }; BD6485812491474500F30986 /* Optimizely.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Optimizely.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C78CAF572445AD8D009FE876 /* OptimizelyJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptimizelyJSON.swift; sourceTree = "<group>"; }; C78CAF652446DB91009FE876 /* OptimizelyClientTests_OptimizelyJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptimizelyClientTests_OptimizelyJSON.swift; sourceTree = "<group>"; }; @@ -2455,6 +2460,7 @@ 6E75167922C520D400B2B157 /* Supporting Files */ = { isa = PBXGroup; children = ( + 98D47DAA2B224627001D38AC /* PrivacyInfo.xcprivacy */, 6E75167A22C520D400B2B157 /* Optimizely.h */, 6E75167B22C520D400B2B157 /* Info.plist */, ); @@ -3292,6 +3298,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 98D47DAC2B224627001D38AC /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3657,6 +3664,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 98D47DAB2B224627001D38AC /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3703,6 +3711,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 98D47DAE2B224627001D38AC /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3710,6 +3719,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 98D47DAD2B224627001D38AC /* PrivacyInfo.xcprivacy in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Package.swift b/Package.swift index ab02c5309..22653f4ee 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,8 @@ -// swift-tools-version:5.0 +// swift-tools-version:5.3 +// The Swift tools version declares the version of the PackageDescription library, +// the minimum version of the Swift tools and Swift language compatibility version to process the manifest, +// and the minimum version of the Swift tools that are needed to use the Swift package. + import PackageDescription let package = Package( @@ -14,7 +18,11 @@ let package = Package( targets: ["Optimizely"]) ], targets: [ - .target(name: "Optimizely", path: "Sources") + .target( + name: "Optimizely", + path: "Sources", + resources: [.copy("Supporting Files/PrivacyInfo.xcprivacy")] + ) ], - swiftLanguageVersions: [.v5] + swiftLanguageVersions: [.v5, .version("5.9")] ) diff --git a/README.md b/README.md index 01b50fc8d..573451918 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# SWIFT SDK +# Optimizely Swift SDK [![Apache 2.0](https://img.shields.io/github/license/nebula-plugins/gradle-extra-configurations-plugin.svg)](http://www.apache.org/licenses/LICENSE-2.0) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/carthage/carthage) [![Build Status](https://travis-ci.com/optimizely/swift-sdk.svg?branch=master)](https://travis-ci.com/optimizely/swift-sdk) @@ -6,25 +6,24 @@ [![Platforms](https://img.shields.io/cocoapods/p/OptimizelySwiftSDK.svg)](https://img.shields.io/cocoapods/p/OptimizelySwiftSDK.svg) [![Podspec](https://img.shields.io/cocoapods/v/OptimizelySwiftSDK.svg)](https://cocoapods.org/pods/OptimizelySwiftSDK) -This repository houses the Swift SDK for use with Optimizely Full Stack and Optimizely Rollouts for Mobile and OTT. +This repository houses the Swift SDK for use with Optimizely Feature Experimentation and Optimizely Full Stack (legacy) for Mobile and OTT. -Optimizely Full Stack is A/B testing and feature flag management for product development teams. Experiment in any application. Make every feature on your roadmap an opportunity to learn. Learn more at https://www.optimizely.com/platform/full-stack/, or see the [documentation](https://docs.developers.optimizely.com/experimentation/v4.0.0-full-stack/docs/welcome). +Optimizely Feature Experimentation is an A/B testing and feature management tool for product development teams, enabling you to experiment at every step. Using Optimizely Feature Experimentation allows for every feature on your roadmap to be an opportunity to discover hidden insights. Learn more at [Optimizely.com](https://www.optimizely.com/products/experiment/feature-experimentation/), or see the [developer documentation](https://docs.developers.optimizely.com/experimentation/v4.0.0-full-stack/docs/welcome). -Optimizely Rollouts is free feature flags for development teams. Easily roll out and roll back features in any application without code deploys. Mitigate risk for every feature on your roadmap. Learn more at https://www.optimizely.com/rollouts/, or see the [documentation](https://docs.developers.optimizely.com/experimentation/v3.1.0-full-stack/docs/introduction-to-rollouts). +Optimizely Rollouts is [free feature flags](https://www.optimizely.com/free-feature-flagging/) for development teams. You can easily roll out and roll back features in any application without code deploys, mitigating risk for every feature on your roadmap. -## Getting Started +## Get started -### Using the SDK +### Use the Swift SDK -See the [Swift SDK developer documentation](https://docs.developers.optimizely.com/experimentation/v4.0.0-full-stack/docs/swift-sdk) to learn how to set -up an Optimizely project and start using the SDK. +Refer to the [Swift SDK's developer documentation](https://docs.developers.optimizely.com/experimentation/v4.0.0-full-stack/docs/swift-sdk) for detailed instructions on getting started with using the SDK. ### Requirements * iOS 10.0+ / tvOS 10.0+ / watchOS 3.0+ * Swift 5+ -### Installing the SDK +### Install the SDK Please note below that _\<platform\>_ is used to represent the platform on which you are building your app. Currently, we support ```iOS```, ```tvOS```, and ```watchOS``` platforms. @@ -38,7 +37,7 @@ Add the dependency on the Optimizely Swift SDK with Swift Package Manager in `Xc #### CocoaPods 1. Add the following lines to the _Podfile_:<pre> ```use_frameworks!``` -```pod 'OptimizelySwiftSDK', '~> 3.10.1'``` +```pod 'OptimizelySwiftSDK', '~> 3.10.5'``` </pre> 2. Run the following command: <pre>``` pod install ```</pre> @@ -46,7 +45,7 @@ Add the dependency on the Optimizely Swift SDK with Swift Package Manager in `Xc Further installation instructions for Cocoapods: https://guides.cocoapods.org/using/getting-started.html #### Carthage -1. Add the following lines to the _Cartfile_:<pre>```github "optimizely/swift-sdk" ~> 3.10.1```</pre> +1. Add the following lines to the _Cartfile_:<pre>```github "optimizely/swift-sdk" ~> 3.10.5```</pre> 2. Run the following command:<pre>```carthage update```</pre> @@ -63,6 +62,12 @@ Further installation instructions for Cocoapods: https://guides.cocoapods.org/us Futher installation instructions for Carthage: https://github.com/Carthage/Carthage +### Feature Management Access + +To access the Feature Management configuration in the Optimizely dashboard, please contact your Optimizely customer success manager. + +## Use the Swift SDK + ### Samples A sample code for SDK initialization and experiments: @@ -80,6 +85,8 @@ optimizely.start{ result in } ``` +See the Optimizely Feature Experimentation [developer documentation](https://docs.developers.optimizely.com/experimentation/v4.0-full-stack/docs/swift-sdk) to learn how to set up your first Swift project and use the SDK. + ### Contributing Please see [CONTRIBUTING](CONTRIBUTING.md). @@ -90,12 +97,34 @@ First-party code (under OptimizelySwiftSDK is copyright Optimizely, Inc. and con ### Additional Code -This software incorporates code from the following open source repo: +This software incorporates code from the following open source projects: -For the SDK: MurmurHash3:https://github.com/jpedrosa/sua/blob/master/Sources/murmurhash3.swift License (Apache 2.0):https://github.com/jpedrosa/sua/blob/master/LICENSE.txt Ported to Swift4. SwiftLint:https://github.com/realm/SwiftLint License (MIT):https://github.com/realm/SwiftLint/blob/master/LICENSE Used to enforce Swift style and conventions. +### Other Optimizely SDKs + +- Agent - https://github.com/optimizely/agent + +- Android - https://github.com/optimizely/android-sdk + +- C# - https://github.com/optimizely/csharp-sdk + +- Flutter - https://github.com/optimizely/optimizely-flutter-sdk + +- Go - https://github.com/optimizely/go-sdk + +- Java - https://github.com/optimizely/java-sdk + +- JavaScript - https://github.com/optimizely/javascript-sdk + +- PHP - https://github.com/optimizely/php-sdk + +- Python - https://github.com/optimizely/python-sdk + +- React - https://github.com/optimizely/react-sdk + +- Ruby - https://github.com/optimizely/ruby-sdk diff --git a/Scripts/build_all.sh b/Scripts/build_all.sh index d5e7b9605..f1fc8bdd0 100755 --- a/Scripts/build_all.sh +++ b/Scripts/build_all.sh @@ -32,7 +32,8 @@ main() { xcodebuild -workspace OptimizelySwiftSDK.xcworkspace -scheme OptimizelySwiftSDK-iOS -configuration Release "${action}" xcodebuild -workspace OptimizelySwiftSDK.xcworkspace -scheme OptimizelySwiftSDK-tvOS -configuration Release "${action}" + xcodebuild -workspace OptimizelySwiftSDK.xcworkspace -scheme OptimizelySwiftSDK-macOS -configuration Release "${action}" + xcodebuild -workspace OptimizelySwiftSDK.xcworkspace -scheme OptimizelySwiftSDK-watchOS -configuration Release "${action}" } main - diff --git a/Scripts/prepare_coveralls_report.sh b/Scripts/prepare_coveralls_report.sh new file mode 100755 index 000000000..adfb0eba0 --- /dev/null +++ b/Scripts/prepare_coveralls_report.sh @@ -0,0 +1,14 @@ +#!/bin/bash -e + +# prepare_coveralls_report.sh +# +# Usage: +# $ ./prepare_coveralls_report.sh +# + +# [coveralls] +# - exclude coverage for Test codes by setting OptimizelySwiftSDK-iOS scheme > Test > Options > Gather coverage for selected targets +mkdir xccov2lcov && cd xccov2lcov && git init && git fetch --depth=1 https://github.com/trax-retail/xccov2lcov.git && git checkout FETCH_HEAD +xcrun xccov view --report --json ../$COVERAGE_DIR/Logs/Test/*.xcresult > coverage.json +swift run xccov2lcov coverage.json > lcov.info +cd .. diff --git a/Scripts/prepare_simulator.sh b/Scripts/prepare_simulator.sh new file mode 100755 index 000000000..29da056dc --- /dev/null +++ b/Scripts/prepare_simulator.sh @@ -0,0 +1,48 @@ +#!/bin/bash -e +set -eou pipefail + +# expects the following environment variables defined +# PLATFORM (eg. iOS Simulator) +# OS (eg. 12.0) +# NAME (eg. iPad Air) +# OS_TYPE (eg. iOS) +# SIMULATOR_XCODE_VERSION (Which Xcode's simulator to use) +# Since github actions only provides limit simulators with each xcode, we need to link simulators from other versions of xcode to be used by current xcode. +# We must use old simulators with current xcode since older xcode versions do not support swift 5 which is required by Swift SDK. +# More about XCode and its compatible simulators can be found here: https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md +# https://github.com/actions/virtual-environments/issues/551 + +# Older than Xcode 12 (12.4?) has different paths +MAJOR_SIMULATOR_XCODE_VERSION=$(echo $SIMULATOR_XCODE_VERSION | cut -d. -f1) +if [ "$MAJOR_SIMULATOR_XCODE_VERSION" -lt 12 ]; then + os_folder="iPhoneOS" + os="${OS/./-}" + name="${NAME//[ ()]/-}" + + sudo mkdir -p /Library/Developer/CoreSimulator/Profiles/Runtimes + + # Check if device is Apple tv, update os_folder for linking purposes + if [[ "$NAME" = "Apple TV"* ]] + then + name="${name}-1080p" + os_folder="AppleTVOS" + fi + + # update os_folder as per xcode version + if [ "$SIMULATOR_XCODE_VERSION" == 10.3 ] + then + os_folder="${os_folder}.platform/Developer/Library" + else + os_folder="${os_folder}.platform/Library/Developer" + fi + + # Link and create simulators from older xcode versions which are not part of the current xcode version + sudo ln -s /Applications/Xcode_$SIMULATOR_XCODE_VERSION.app/Contents/Developer/Platforms/$os_folder/CoreSimulator/Profiles/Runtimes/$OS_TYPE.simruntime /Library/Developer/CoreSimulator/Profiles/Runtimes/$OS_TYPE\ $OS.simruntime + xcrun simctl create "custom-device" "com.apple.CoreSimulator.SimDeviceType.$name" "com.apple.CoreSimulator.SimRuntime.$OS_TYPE-$os" + CUSTOM_SIMULATOR="$(instruments -s devices | grep -m 1 'custom-device' | awk -F'[][]' '{print $2}')" +else + echo ".devices.\"com.apple.CoreSimulator.SimRuntime.${PLATFORM/ Simulator/}-${OS/./-}\"" > /tmp/jq_file + CUSTOM_SIMULATOR=$( xcrun simctl list --json devices | jq -f /tmp/jq_file | jq -r '.[] | select(.name==env.NAME) | .udid' ) +fi +xcrun simctl boot $CUSTOM_SIMULATOR && sleep 30 +xcrun simctl list | grep Booted diff --git a/Scripts/run_prep.sh b/Scripts/run_prep.sh index 7209e9caa..dace39b25 100755 --- a/Scripts/run_prep.sh +++ b/Scripts/run_prep.sh @@ -1,19 +1,16 @@ -#!/usr/bin/env bash -set -e +#!/bin/bash -e -# Because `hub` is used, this script expects the following environment variables defined in travis job settings: +# Because `hub` is used, this script expects the following environment variables: # GITHUB_TOKEN - github api token with repo permissions (display value in build log setting: OFF) # GITHUB_USER - github username that GITHUB_TOKEN is associated with (display value in build log setting: ON) # Additionally, it needs the following environment variables: -# VERSION - defined in .travis.yml - -# Variables starting with TRAVIS_ are default environment variables available to all Travis CI builds +# VERSION - defined in swift.yml COLOR_RESET='\033[0m' COLOR_MAGENTA='\033[0;35m' COLOR_CYAN='\033[0;36m' -MYREPO=${HOME}/workdir/${TRAVIS_REPO_SLUG} +MYREPO=${HOME}/workdir/${REPO_SLUG} AUTOBRANCH=${GITHUB_USER}/prepareRelease${VERSION} BUILD_OUTPUT=/tmp/build.out touch $BUILD_OUTPUT @@ -21,7 +18,7 @@ touch $BUILD_OUTPUT function prep_workspace { rm -rf ${MYREPO} mkdir -p ${MYREPO} - git clone -b ${TRAVIS_BRANCH} https://${GITHUB_TOKEN}@github.com/${TRAVIS_REPO_SLUG} ${MYREPO} + git clone -b ${BRANCH} https://${GITHUB_TOKEN}@github.com/${REPO_SLUG} ${MYREPO} cd ${MYREPO} git checkout -b ${AUTOBRANCH} } @@ -38,16 +35,27 @@ function error_handler() { } function do_stuff { - # keepalive for Travis while :; do sleep 10; echo -n .; done & trap "kill $!" EXIT trap 'error_handler' ERR # we need pod install or test_all.sh fails - pod repo update - pod install + + # we skip "test_all.sh" until we have a good reason to repeat it heere + # 1. this test takes long and also flaky tests can interrupt release process + # 2. this "test_all.sh" is supposed to pass before starting pre-release + # 3. prep auto PRs will be tested in CI/CD before starting release process. + + # - cocoapods requires ENV['HOME'] with absolute path + # + # HOME=$(pwd) + # gem install cocoapods -v $COCOAPODS_VERSION + # pod _${COCOAPODS_VERSION}_ repo update + # pod _${COCOAPODS_VERSION}_ install + # + # myscripts=( "update_version.sh ${VERSION}" "build_all.sh" "test_all.sh" ) + myscripts=( "update_version.sh ${VERSION}" "build_all.sh" ) - myscripts=( "update_version.sh ${VERSION}" "build_all.sh" "test_all.sh" ) for i in "${myscripts[@]}"; do echo -n "${i} " echo "===== ${i} =====" >> $BUILD_OUTPUT @@ -60,11 +68,23 @@ function do_stuff { } function push_changes { + + git config user.email "optibot@users.noreply.github.com" git config user.name "${GITHUB_USER}" git add --all - # this is like a try/catch - git commit -m "ci(travis): auto release prep for $VERSION" || + + TITLE="ci(git-action): auto release prep for $VERSION" + # an empty line required between title and description + # a dummy ref (FSSDK-1234) for required FSSDK checking + MESSAGE=$(cat <<END +${TITLE} + +- [FSSDK-1234] +END +) + + git commit -m "${TITLE}" || { case $? in 1 ) @@ -77,10 +97,12 @@ function push_changes { ;; esac } - git push https://${GITHUB_TOKEN}@github.com/${TRAVIS_REPO_SLUG} ${AUTOBRANCH} - PR_URL=$(hub pull-request --no-edit -b ${TRAVIS_BRANCH}) + git push -f https://${GITHUB_TOKEN}@github.com/${REPO_SLUG} ${AUTOBRANCH} + + hub version + PR_URL=$(hub pull-request -b ${BRANCH} -h ${AUTOBRANCH} -m "${MESSAGE}") echo -e "${COLOR_CYAN}ATTENTION:${COLOR_RESET} review and merge ${COLOR_CYAN}${PR_URL}${COLOR_RESET}" - echo "then to release to cocoapods use Travis CI's Trigger build with the following payload:" + echo "then to release to cocoapods use Git action's Trigger build with the following payload:" echo -e "${COLOR_MAGENTA}env:${COLOR_RESET}" echo -e "${COLOR_MAGENTA} - RELEASE=true${COLOR_RESET}" } diff --git a/Scripts/run_release.sh b/Scripts/run_release.sh index c10724b6b..ba30c1146 100755 --- a/Scripts/run_release.sh +++ b/Scripts/run_release.sh @@ -1,13 +1,85 @@ #!/usr/bin/env bash set -e -# Because `hub` is used, this script expects the following environment variables defined in travis job settings: +# Because `hub` is used, this script expects the following environment variables: # GITHUB_TOKEN - github api token with repo permissions (display value in build log setting: OFF) # GITHUB_USER - github username that GITHUB_TOKEN is associated with (display value in build log setting: ON) # COCOAPODS_TRUNK_TOKEN - should be defined in job settings so that we can `pod trunk push` +MYREPO=${HOME}/workdir/${REPO_SLUG} + +ARCH="amd64" +OS="$(uname -s | tr '[:upper:]' '[:lower:]')" +case "$OS" in +mingw* | msys* ) OS=windows ;; +esac + +[[ $OS == 'windows' ]] && windows=1 + +download() { + case "$OS" in + windows ) + WINDOWS_URL=$(curl -u $GITHUB_USER:$GITHUB_TOKEN https://api.github.com/repos/$1/$2/releases/latest 2>/dev/null | jq -r '.assets[] | select(.browser_download_url | contains("windows-amd64")) | .browser_download_url') + echo "$WINDOWS_URL" + curl -fsSLO "$WINDOWS_URL" + unzip "$(basename "$WINDOWS_URL")" bin/hub.exe + rm -f "$(basename "$WINDOWS_URL")" + ;; + darwin ) + DARWIN_URL=$(curl -u $GITHUB_USER:$GITHUB_TOKEN https://api.github.com/repos/$1/$2/releases/latest 2>/dev/null | jq -r '.assets[] | select(.browser_download_url | contains("darwin-amd64")) | .browser_download_url') + curl -fsSL "$DARWIN_URL" -o - | tar xz --strip-components=1 '*/bin/hub' + ;; + * ) + LINUX_URL=$(curl -u $GITHUB_USER:$GITHUB_TOKEN https://api.github.com/repos/$1/$2/releases/latest 2>/dev/null | jq -r '.assets[] | select(.browser_download_url | contains("linux-amd64")) | .browser_download_url') + curl -fsSL "$LINUX_URL" | tar xz --strip-components=1 --wildcards '*/bin/hub' + ;; + esac +} + +function install_binary { + mkdir -p ~/bin + + # https://code-maven.com/create-temporary-directory-on-linux-using-bash + tmp_dir=$(mktemp -d -t ci-XXXXXXXXXX) + + cd $tmp_dir + + download $1 $2 + + if [ ! -f "$tmp_dir/bin/hub${windows:+.exe}" ]; then + echo "Failed to obtain $tmp_dir/bin/hub${windows:+.exe}" + exit 1 + fi + mkdir -p ~/bin/ + mv $tmp_dir/bin/hub${windows:+.exe} ~/bin/ + + chmod +x ~/bin/hub${windows:+.exe} + pwd + # verify + ~/bin/hub${windows:+.exe} version + + # cleanup + rm -rf $tmp_dir + echo "hub installed" + # cp ~/bin/hub . +} + +function prep_workspace { + rm -rf ${MYREPO} + mkdir -p ${MYREPO} + git clone -b ${BRANCH} https://${GITHUB_TOKEN}@github.com/${REPO_SLUG} ${MYREPO} + cd ${MYREPO} +} + function release_github { + LAST_RELEASE=$(git describe --abbrev=0 --tags) + + if [[ ${LAST_RELEASE} == "v${VERSION}" ]]; then + echo "${LAST_RELEASE} tag exists already (probably created while in the current release process). Skipping..." + return + fi + CHANGELOG="CHANGELOG.md" # check that CHANGELOG.md has been updated @@ -21,12 +93,15 @@ function release_github { LAST_VERSION=$(grep '^## \d\+\.\d\+.\d\+' ${CHANGELOG} | awk 'NR==2') DESCRIPTION=$(awk "/^${NEW_VERSION}$/,/^${LAST_VERSION:-nothingmatched}$/" ${CHANGELOG} | grep -v "^${LAST_VERSION:-nothingmatched}$") - - hub release create v${VERSION} -m "Release ${VERSION}" -m "${DESCRIPTION}" -t "${TRAVIS_BRANCH}" - + hub version + hub release create v${VERSION} -m "Release ${VERSION}" -m "${DESCRIPTION}" -t "${BRANCH}" } function release_cocoapods { + + # - cocoapods requires ENV['HOME'] with absolute path + HOME=$(pwd) + gem install cocoapods -v $COCOAPODS_VERSION # ---- Optimizely's pods ---- pods=(OptimizelySwiftSDK); @@ -39,13 +114,14 @@ function release_cocoapods { do podname=${pods[i]}; printf "Pushing the ${podname} pod to COCOAPODS.ORG .\n" - pod trunk push --allow-warnings ${podname}.podspec - pod update + pod _${COCOAPODS_VERSION}_ trunk push --allow-warnings ${podname}.podspec + pod _${COCOAPODS_VERSION}_ update done } function main { + prep_workspace release_github release_cocoapods } diff --git a/Scripts/run_unit_tests.sh b/Scripts/run_unit_tests.sh index bea414251..dd99210a2 100755 --- a/Scripts/run_unit_tests.sh +++ b/Scripts/run_unit_tests.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -e # run xcode unit tests # @@ -7,7 +7,7 @@ # # unit tests for PR only -if [[ "$TRAVIS_BRANCH" == "master" ]] -then +# if [[ "$BRANCH" == "master" ]] +# then xcodebuild test -derivedDataPath $COVERAGE_DIR -workspace OptimizelySwiftSDK.xcworkspace -scheme $SCHEME -configuration Release CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO -sdk $TEST_SDK -destination "platform=$PLATFORM,OS=$OS,name=$NAME" ONLY_ACTIVE_ARCH=YES | tee buildoutput | xcpretty && test ${PIPESTATUS[0]} -eq 0 -fi +# fi diff --git a/Scripts/start_simulator.sh b/Scripts/start_simulator.sh deleted file mode 100755 index 7cbc9bc09..000000000 --- a/Scripts/start_simulator.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash -set -eou pipefail - -# expects the following environment variables defined -# PLATFORM (eg. iOS Simulator) -# OS (eg. 12.0) -# NAME (eg. iPad Air) - -# prep jq arg because it doesnt allow parameter expansion within its single quotes -echo ".devices.\"com.apple.CoreSimulator.SimRuntime.${PLATFORM/ Simulator/}-${OS/./-}\"" > /tmp/jq_file - -simulator=$( xcrun simctl list --json devices | jq -f /tmp/jq_file | jq -r '.[] | select(.name==env.NAME) | .udid' ) -if [ -z $simulator ]; then - echo "The requested simulator ($PLATFORM $OS $NAME) cannot be found." - #xcrun instruments -s device - xcrun xctrace list devices - sleep 3 - exit 1 -fi - -xcrun simctl boot $simulator && sleep 30 -xcrun simctl list | grep Booted diff --git a/Scripts/test_all.sh b/Scripts/test_all.sh index 36e12d960..1833f8227 100755 --- a/Scripts/test_all.sh +++ b/Scripts/test_all.sh @@ -1,13 +1,25 @@ -echo 'Testing OptimizelySwiftSDK-iOS (iPhone 8,OS=12.1)' -xcrun xcodebuild -workspace OptimizelySwiftSDK.xcworkspace -scheme OptimizelySwiftSDK-iOS -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 8,OS=12.1' test -echo 'Testing OptimizelySwiftSDK-iOS (iPhone SE,OS=11.1)' -xcrun xcodebuild -workspace OptimizelySwiftSDK.xcworkspace -scheme OptimizelySwiftSDK-iOS -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone SE,OS=11.1' test -echo 'Testing OptimizelySwiftSDK-iOS (iPhone XS,OS=13.2)' -xcrun xcodebuild -workspace OptimizelySwiftSDK.xcworkspace -scheme OptimizelySwiftSDK-iOS -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone XS,OS=13.2' test -echo 'Testing OptimizelySwiftSDK-tvOS (Apple TV,OS=11.1)' -xcrun xcodebuild -workspace OptimizelySwiftSDK.xcworkspace -scheme OptimizelySwiftSDK-tvOS -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV,OS=11.1' test -echo 'Testing OptimizelySwiftSDK-tvOS (Apple TV,OS=12.1)' -xcrun xcodebuild -workspace OptimizelySwiftSDK.xcworkspace -scheme OptimizelySwiftSDK-tvOS -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV,OS=12.1' test -echo 'Testing OptimizelySwiftSDK-tvOS (Apple TV 4K,OS=13.2)' -xcrun xcodebuild -workspace OptimizelySwiftSDK.xcworkspace -scheme OptimizelySwiftSDK-tvOS -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV 4K,OS=13.2' test +#!/bin/bash -e +# Since github actions only provides limit simulators with each xcode, we need to link simulators from other versions of xcode to be used by current xcode. +# We must use old simulators with current xcode since older xcode versions do not support swift 5 which is required by Swift SDK. +# More about XCode and its compatible simulators can be found here: https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md +# https://github.com/actions/virtual-environments/issues/551 + +deviceModels=("iPhone 12" "iPhone 8" "iPad Air (4th generation)" "Apple TV") +osVersions=("16.0" "14.4" "15.0" "16.0") +xcodeVersions=("14.3" "14.3" "14.3" "14.3") +platforms=("iOS" "iOS" "iOS" "tvOS") +testSdks=("iphonesimulator" "iphonesimulator" "iphonesimulator" "appletvsimulator") + +for i in "${!deviceModels[@]}"; do + export PLATFORM="${platforms[$i]} Simulator" + export OS="${osVersions[$i]}" + export NAME="${deviceModels[$i]}" + export OS_TYPE="${platforms[$i]}" + export SIMULATOR_XCODE_VERSION="${xcodeVersions[$i]}" + + Scripts/prepare_simulator.sh + echo "Testing OptimizelySwiftSDK-${platforms[$i]} (${deviceModels[$i]},OS=${osVersions[$i]})" + + xcrun xcodebuild -workspace OptimizelySwiftSDK.xcworkspace -scheme "OptimizelySwiftSDK-${platforms[$i]}" -sdk "${testSdks[$i]}" -configuration Release -destination "platform=${platforms[$i]} Simulator,name=${deviceModels[$i]},OS=${osVersions[$i]}" test +done diff --git a/Scripts/upload_coveralls.sh b/Scripts/upload_coveralls.sh deleted file mode 100755 index c74239c1f..000000000 --- a/Scripts/upload_coveralls.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -# upload_coveralls.sh -# -# Usage: -# $ ./upload_coveralls.sh -# - -# [coveralls] -# - exclude coverage for Test codes by setting OptimizelySwiftSDK-iOS scheme > Test > Options > Gather coverage for selected targets -# - report coverage for PR and iPhone 11 only (avoid redundant ones) -# - use Xcode12.4+ (older Xcode reports a wrong number) -if [[ "$TRAVIS_BRANCH" == "master" && "$PLATFORM" == "iOS Simulator" && "$NAME" == "iPhone 11" ]] -then - mkdir xccov2lcov && cd xccov2lcov && git init && git fetch --depth=1 https://github.com/trax-retail/xccov2lcov.git && git checkout FETCH_HEAD - xcrun xccov view --report --json ../$COVERAGE_DIR/Logs/Test/*.xcresult > coverage.json - swift run xccov2lcov coverage.json > lcov.info - - cd .. - coveralls-lcov -v --repo-token $COVERALLS_TOKEN xccov2lcov/lcov.info -fi diff --git a/Sources/Customization/DefaultEventDispatcher.swift b/Sources/Customization/DefaultEventDispatcher.swift index 68e93feae..0311f9d09 100644 --- a/Sources/Customization/DefaultEventDispatcher.swift +++ b/Sources/Customization/DefaultEventDispatcher.swift @@ -183,7 +183,6 @@ open class DefaultEventDispatcher: BackgroundingCallbacks, OPTEventDispatcher { var request = URLRequest(url: event.url) request.httpMethod = "POST" - request.httpBody = event.body request.addValue("application/json", forHTTPHeaderField: "Content-Type") // send notification BEFORE sending event to the server diff --git a/Sources/Optimizely/OptimizelyClient.swift b/Sources/Optimizely/OptimizelyClient.swift index a88fd9de0..0c32c7eb0 100644 --- a/Sources/Optimizely/OptimizelyClient.swift +++ b/Sources/Optimizely/OptimizelyClient.swift @@ -745,7 +745,7 @@ open class OptimizelyClient: NSObject { public func getOptimizelyConfig() throws -> OptimizelyConfig { guard let config = self.config else { throw OptimizelyError.sdkNotReady } - return OptimizelyConfigImp(projectConfig: config) + return OptimizelyConfigImp(projectConfig: config, logger: logger) } } diff --git a/Sources/Optimizely/OptimizelyConfig.swift b/Sources/Optimizely/OptimizelyConfig.swift index af590d31b..e04d3002c 100644 --- a/Sources/Optimizely/OptimizelyConfig.swift +++ b/Sources/Optimizely/OptimizelyConfig.swift @@ -95,8 +95,8 @@ struct OptimizelyConfigImp: OptimizelyConfig { var attributes: [OptimizelyAttribute] = [] var audiences: [OptimizelyAudience] = [] var events: [OptimizelyEvent] = [] - - init(projectConfig: ProjectConfig) { + + init(projectConfig: ProjectConfig, logger: OPTLogger = DefaultLogger()) { guard let project = projectConfig.project else { return } self.environmentKey = project.environmentKey ?? "" @@ -139,7 +139,7 @@ struct OptimizelyConfigImp: OptimizelyConfig { return updatedRollout } - self.experimentsMap = makeExperimentsMap(project: project, experiments: updatedExperiments) + self.experimentsMap = makeExperimentsMap(project: project, experiments: updatedExperiments, logger: logger) self.featuresMap = makeFeaturesMap(project: project, experiments: updatedExperiments, rollouts: updatedRollouts) } } @@ -148,9 +148,12 @@ struct OptimizelyConfigImp: OptimizelyConfig { extension OptimizelyConfigImp { - func makeExperimentsMap(project: Project, experiments: [Experiment]) -> [String: Experiment] { + func makeExperimentsMap(project: Project, experiments: [Experiment], logger: OPTLogger) -> [String: Experiment] { var map = [String: Experiment]() experiments.forEach { + if map.keys.contains($0.key) { + logger.w("Duplicate experiment keys found in datafile: \($0.key)") + } map[$0.key] = $0 } return map diff --git a/Sources/Supporting Files/PrivacyInfo.xcprivacy b/Sources/Supporting Files/PrivacyInfo.xcprivacy new file mode 100644 index 000000000..8042b2096 --- /dev/null +++ b/Sources/Supporting Files/PrivacyInfo.xcprivacy @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>NSPrivacyCollectedDataTypes</key> + <array> + <dict> + <key>NSPrivacyCollectedDataType</key> + <string>NSPrivacyCollectedDataTypeProductInteraction</string> + <key>NSPrivacyCollectedDataTypeLinked</key> + <false/> + <key>NSPrivacyCollectedDataTypeTracking</key> + <false/> + <key>NSPrivacyCollectedDataTypePurposes</key> + <array> + <string>NSPrivacyCollectedDataTypePurposeAnalytics</string> + </array> + </dict> + </array> + <key>NSPrivacyAccessedAPITypes</key> + <array> + <dict> + <key>NSPrivacyAccessedAPIType</key> + <string>NSPrivacyAccessedAPICategoryUserDefaults</string> + <key>NSPrivacyAccessedAPITypeReasons</key> + <array> + <string>CA92.1</string> + </array> + </dict> + </array> +</dict> +</plist> diff --git a/Sources/Utils/SDKVersion.swift b/Sources/Utils/SDKVersion.swift index 2ef3b49b4..72afe1366 100644 --- a/Sources/Utils/SDKVersion.swift +++ b/Sources/Utils/SDKVersion.swift @@ -17,4 +17,4 @@ /// Do not edit this field. /// - It is auto updated (Scripts/updated_version.sh) to reflect the current version /// - Do not put underscores in the name (Swiftlint can modify unexpectedly) -let OPTIMIZELYSDKVERSION = "3.10.1" +let OPTIMIZELYSDKVERSION = "3.10.5" diff --git a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_OptimizelyConfig.swift b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_OptimizelyConfig.swift index 24880abfa..48891924d 100644 --- a/Tests/OptimizelyTests-APIs/OptimizelyClientTests_OptimizelyConfig.swift +++ b/Tests/OptimizelyTests-APIs/OptimizelyClientTests_OptimizelyConfig.swift @@ -263,6 +263,64 @@ class OptimizelyClientTests_OptimizelyConfig: XCTestCase { let result = try? self.optimizely.getOptimizelyConfig() XCTAssertNil(result) } + + func testOptimizelyConfigWithDuplicateKeys() { + let exp0: [String : Any] = [ + "id": "10001", + "key": "duplicate_key", + "status": "Running", + "layerId": "22222", + "variations": [], + "trafficAllocation": [], + "audienceIds": ["33333"], + "audienceConditions": [], + "forcedVariations": ["12345": "1234567890"] + ] + + let exp1: [String : Any] = [ + "id": "10005", + "key": "duplicate_key", + "status": "Running", + "layerId": "22222", + "variations": [], + "trafficAllocation": [], + "audienceIds": ["33333"], + "audienceConditions": [], + "forcedVariations": ["12345": "1234567890"] + ] + + var projectData: [String: Any] = [ + "version": "4", + "projectId": "11111", + "experiments": [], + "audiences": [], + "groups": [], + "attributes": [], + "accountId": "1234567890", + "events": [], + "revision": "5", + "anonymizeIP": true, + "rollouts": [], + "typedAudiences": [], + "integrations": [], + "featureFlags": [], + "botFiltering": false, + "sendFlagDecisions": true + ] + + projectData["experiments"] = [exp0, exp1] + let model: Project = try! OTUtils.model(from: projectData) + let projectConfig = ProjectConfig() + projectConfig.project = model + + let logger = TestLogger() + let optiConfigImpl = OptimizelyConfigImp(projectConfig: projectConfig, logger: logger) + let optimizelyExpMap: [String: OptimizelyExperiment] = optiConfigImpl.experimentsMap + XCTAssertEqual(logger.getMessages(.warning), ["Duplicate experiment keys found in datafile: duplicate_key"]) + + XCTAssertEqual(optimizelyExpMap.count, 1) + XCTAssertEqual(optimizelyExpMap["duplicate_key"]?.id, "10005") + } } @@ -371,3 +429,41 @@ extension OptimizelyEvent { } } +// MARK: - Mock Loggers + +fileprivate class TestLogger: OPTLogger { + private static var _logLevel: OptimizelyLogLevel? + public static var logLevel: OptimizelyLogLevel { + get { + return _logLevel ?? .info + } + set (newLevel) { + _logLevel = newLevel + } + } + + required public init() { + clearMessages() + } + + func log(level: OptimizelyLogLevel, message: String) { + logMessages[level.rawValue].append(message) + } + + // Utils + + var logMessages = [[String]]() + + var logCount: Int { + return logMessages.reduce(0) { $0 + $1.count } + } + + func getMessages(_ level: OptimizelyLogLevel) -> [String] { + return logMessages[level.rawValue] + } + + func clearMessages() { + logMessages = [[String]](repeating: [], count: OptimizelyLogLevel.debug.rawValue + 1) + } + +}