diff --git a/.github/gptdriverrunscript.sh b/.github/gptdriverrunscript.sh new file mode 100644 index 000000000..2bb42b1bc --- /dev/null +++ b/.github/gptdriverrunscript.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash + +set -eo pipefail + +# Constants should ideally be set as environment variables for security +readonly API_URL="https://api.mobileboost.io" +readonly API_ORG_KEY="${API_ORG_KEY}" +readonly API_TOKEN="${API_TOKEN:-null}" +readonly TEST_TIMEOUT="${TEST_TIMEOUT:-7200}" +readonly TEST_TAGS="${TEST_TAGS:-}" + +# Function to post data using curl and handle errors +post_data() { + local url=$1 + local body=$2 + if ! response=$(curl -s -f -X POST -H "Authorization: Bearer $API_TOKEN" -H "Content-Type: application/json" -d "$body" "$url"); then + echo "Error: Network request failed with error $response" >&2 + exit 1 + fi + echo "$response" +} + +# Validate inputs +if [[ -z "$1" || -z "$2" ]]; then + echo "Usage: $0 " + exit 1 +fi + +# Validate environment variables +if [[ -z "$API_ORG_KEY" ]]; then + echo "Please set API_ORG_KEY to your organization key" + exit 1 +fi + +buildFilename="$1" +buildPlatform="$2" +tags=() + +# Check if TEST_TAGS is provided and split into an array +if [[ -n "$TEST_TAGS" ]]; then + IFS=',' read -ra tags <<< "$TEST_TAGS" +fi + +# Upload build file +echo -n "Uploading build from $buildFilename for $buildPlatform: " +if ! uploadedBuildResponse=$(curl -s -f -X POST \ + -H "Authorization: Bearer $API_TOKEN" \ + -H "Content-Type: multipart/form-data" \ + -F "build=@$buildFilename" \ + -F "organisation_key=$API_ORG_KEY" \ + -F "platform=$buildPlatform" \ + -F "metadata={}" \ + "$API_URL/uploadBuild/"); then + echo "Error: Failed to upload build" >&2 + exit 1 +fi + +# Extract the buildId +if ! buildId=$(jq -r '.buildId' <<< "$uploadedBuildResponse") || [ -z "$buildId" ]; then + echo "Error: Failed to extract build ID from the response" >&2 + exit 1 +fi +echo "uploaded (ID: $buildId), app link: $(jq -r '.app_link' <<< "$uploadedBuildResponse")" + +# Execute test suite +echo "Executing test suite..." +jsonPayload="{\"organisationId\": \"$API_ORG_KEY\", \"uploadId\": \"$buildId\"" +if [ ${#tags[@]} -gt 0 ]; then + jsonTags=$(printf ',\"%s\"' "${tags[@]}") + jsonTags="[${jsonTags:1}]" + jsonPayload+=", \"tags\": $jsonTags" +fi +jsonPayload+="}" +if ! testSuiteRunId=$(post_data "$API_URL/tests/execute" "$jsonPayload" | jq -r '.test_suite_ids[0]') || [ -z "$testSuiteRunId" ]; then + echo "Error: Test suite execution failed" >&2 + exit 1 +fi + +# Wait for test suite to finish +echo -n "Waiting for test suite to finish..." +startTime=$(date +%s) +while true; do + if ! testSuiteData=$(curl -s -f "$API_URL/testSuiteRuns/$testSuiteRunId/gh"); then + echo "Error: Failed to retrieve test suite data" >&2 + exit 1 + fi + testSuiteStatus=$(jq -r '.status' <<< "$testSuiteData") + + if [[ + "$testSuiteStatus" == "completed" + ]]; then + echo "Status is $testSuiteStatus!" >&2 + break + fi + + if (( $(date +%s) - startTime >= TEST_TIMEOUT )); then + echo "Timeout exceeded while waiting for test suite to finish." >&2 + exit 1 + fi + + echo -n "." + sleep 1 +done +echo " done!" + +# Write test suite summary to file if available +if [[ -n "$GITHUB_STEP_SUMMARY" && -w "$GITHUB_STEP_SUMMARY" ]]; then + jq -r '.markdown' <<< "$testSuiteData" >> "$GITHUB_STEP_SUMMARY" + echo "Step summary written to $GITHUB_STEP_SUMMARY" +fi + +# Check test suite result +if ! testSuiteResult=$(jq -r '.result' <<< "$testSuiteData"); then + echo "Test suite did not pass, result: $testSuiteResult" >&2 + exit 1 +fi + +if [[ "$testSuiteResult" == "succeeded" ]]; then + echo "Test passed successfully" + exit 0 +else + echo "Test suite did not pass, result: $testSuiteResult" >&2 + exit 1 +fi diff --git a/.github/workflows/gpt-driver-tests.yml b/.github/workflows/gpt-driver-tests.yml new file mode 100644 index 000000000..99ece3fb7 --- /dev/null +++ b/.github/workflows/gpt-driver-tests.yml @@ -0,0 +1,149 @@ +name: GPTDriver Test Suite Automation + +on: + workflow_dispatch: + inputs: + push: + branches: + - 'Release-*' # Trigger for branches starting with "Release-" + +jobs: + BuildAndTestAppOnGPTDriver: + runs-on: macos-latest + steps: + # --- Step 1: Extract version from branch name --- + - name: Extract version from branch name + id: extract_version_step + run: | + BRANCH_NAME="${{ github.ref }}" + # Remove 'refs/heads/' prefix (e.g., refs/heads/Release-0.0.0 -> Release-0.0.0) + BRANCH_NAME_WITHOUT_PREFIX="${BRANCH_NAME#refs/heads/}" + # Extract version after "Release-" (e.g., Release-0.0.0 -> 0.0.0) + VERSION=$(echo "$BRANCH_NAME_WITHOUT_PREFIX" | sed -n 's/^Release-\([0-9]*\.[0-9]*\.[0-9]*\)$/\1/p') + + if [ -z "$VERSION" ]; then + echo "Error: Could not extract version from branch name '$BRANCH_NAME_WITHOUT_PREFIX'. Expected format: Release-X.Y.Z" + exit 1 + fi + + echo "Extracted versionName: $VERSION" + echo "VERSION_STRING=$VERSION" >> $GITHUB_ENV + + # Convert semantic version to an integer for CFBundleVersion (versionCode equivalent) + # Example: 1.2.3 -> 102003 (assuming max 2 digits for minor/patch) + # This should be adjusted based on the maximum expected values for major/minor/patch + MAJOR=$(echo "$VERSION" | cut -d. -f1) + MINOR=$(echo "$VERSION" | cut -d. -f2) + PATCH=$(echo "$VERSION" | cut -d. -f3) + + # Calculate versionCode (CFBundleVersion) - ensure this fits in a 32-bit integer + # Standard Android-like conversion: Major * 10000 + Minor * 100 + Patch + # This provides sufficient uniqueness for most common versioning schemes. + VERSION_CODE_INT=$(( MAJOR * 10000 + MINOR * 100 + PATCH )) + echo "Calculated versionCode: $VERSION_CODE_INT" + echo "VERSION_CODE_INT=$VERSION_CODE_INT" >> $GITHUB_ENV + + + # --- Step 2: Checkout the iOS Branch SDK repository --- + - name: Checkout BranchMetrics/ios-branch-deep-linking-attribution (SDK) + uses: actions/checkout@v4 + with: + repository: BranchMetrics/ios-branch-deep-linking-attribution + ref: ${{ github.ref }} # Use the same branch that triggered the workflow + path: ./branch-ios-sdk-repo # Checkout into a subdirectory + + # --- Step 3: Build the iOS Branch SDK Framework --- + - name: Build Branch SDK Framework + run: | + # Build for simulator. Adjust scheme if necessary. + # The output framework will be in build/Debug-iphonesimulator/BranchSDK.framework + xcodebuild -scheme xcframework \ + BUILD_DIR="${{ github.workspace }}/branch-ios-sdk-repo/build" + working-directory: ./branch-ios-sdk-repo + + # --- Step 4: Checkout the iOS Branch Link Simulator App repository --- + - name: Checkout BranchMetrics/BranchLinkSimulator (App) + uses: actions/checkout@v4 + with: + repository: BranchMetrics/BranchLinkSimulator + ref: gptdriver/linkingTests # Checkout the specific app branch + path: ./ios-app-repo # Checkout into another subdirectory + + # --- Step 5: Copy the generated SDK Framework to the App's project --- + - name: Copy generated SDK Framework to App's libs directory + run: | + # Create a 'Frameworks' directory within the app repo for the local SDK + mkdir -p ./ios-app-repo/Frameworks + # Copy the built framework + cp -R ./branch-ios-sdk-repo/build/BranchSDK.xcframework ./ios-app-repo/Frameworks/ + working-directory: ${{ github.workspace }} # Run from the root of the GITHUB_WORKSPACE + + # --- Step 6: Install Code Sign Certificate and Provisioning profile + - name: Install the Apple certificate and provisioning profile + env: + BUILD_CERTIFICATE_BASE64: ${{ secrets.BS_BUILD_CERTIFICATE }} + P12_PASSWORD: ${{ secrets.BS_P12_PASSWORD }} + BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BS_BUILD_PROVISION_PROFILE_BASE64_PART_AA }} + KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} + run: | + # create variables + CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12 + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision + + # import certificate + echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH + + # Create Provisioning Profiles + echo "${{ secrets.BS_BUILD_PROVISION_PROFILE_BASE64_PART_AA }}" >> part_aa + #echo "${{ secrets.BS_BUILD_PROVISION_PROFILE_BASE64_PART_AB }}" >> part_aa + #echo "${{ secrets.BS_BUILD_PROVISION_PROFILE_BASE64_PART_AC }}" >> part_aa + base64 -D -i part_aa > $PP_PATH + + # create temporary keychain + security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + + # import certificate to keychain + security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH + + # apply provisioning profile + mkdir -p ~/Library/Developer/Xcode/UserData/Provisioning\ Profiles + cp $PP_PATH ~/Library/Developer/Xcode/UserData/Provisioning\ Profiles/ + + # --- Step 6: Build the iOS Branch Link Simulator App using the local SDK Framework --- + - name: Build iOS App with local SDK + run: | + # Build the app. Adjust project/workspace, scheme, and destination if necessary. + # We're passing MARKETING_VERSION (versionName) and CURRENT_PROJECT_VERSION (versionCode) + xcodebuild -scheme BranchLinkSimulator -allowProvisioningUpdates \ + MARKETING_VERSION=${{ env.VERSION_STRING }} \ + CURRENT_PROJECT_VERSION=${{ env.VERSION_CODE_INT }} \ + -sdk iphoneos archive -archivePath ./IPA/BranchLinkSimulator.xcarchive + xcodebuild -exportArchive -archivePath ./IPA/BranchLinkSimulator.xcarchive -exportOptionsPlist IPA/Info.plist -exportPath IPA/ + working-directory: ./ios-app-repo + + # --- Step 7: Echo the location of the generated .app bundle --- + - name: Echo .app bundle location + run: | + APP_PATH="./ios-app-repo/IPA/BranchLinkSimulator.ipa" + echo "Generated IPA location: $APP_PATH" + + # --- Step 8: Upload Build Artifacts --- + - name: Upload Build Artifacts + uses: actions/upload-artifact@v4 + with: + name: BranchLinkSimulator-iOS-Debug-Build + path: ./ios-app-repo/IPA/BranchLinkSimulator.ipa + + # --- Step 9: Run tests on GPTDriver service. --- + - name: Run GPTDriver tests + run: | + chmod +x ./branch-ios-sdk-repo/.github/gptdriverrunscript.sh + ./branch-ios-sdk-repo/.github/gptdriverrunscript.sh ./ios-app-repo/IPA/BranchLinkSimulator.ipa ios + env: + API_ORG_KEY: ${{ secrets.MOBILEBOOST_API_ORG_KEY }} + API_KEY: ${{ secrets.MOBILEBOOST_API_ORG_KEY }} + TEST_TAGS: ios