diff --git a/.github/composite_actions/get_platform_parameters/action.yml b/.github/composite_actions/get_platform_parameters/action.yml index 55d1300511..bd222f8aef 100644 --- a/.github/composite_actions/get_platform_parameters/action.yml +++ b/.github/composite_actions/get_platform_parameters/action.yml @@ -5,7 +5,7 @@ inputs: required: true type: string xcode_version: - description: "The version of Xcode. Available aliases are 'latest' and 'minimum'" + description: "The version of Xcode. Available aliases are 'latest', 'minimum', and 'beta'" default: 'latest' type: string destination: @@ -41,8 +41,9 @@ runs: - id: get-xcode-version run: | - LATEST_XCODE_VERSION=16.2.0 - MINIMUM_XCODE_VERSION=16.1.0 + LATEST_XCODE_VERSION=16.4.0 + MINIMUM_XCODE_VERSION=16.0.0 + DEFAULT_BETA_XCODE_VERSION=26.0_beta INPUT_XCODE_VERSION=${{ inputs.xcode_version }} @@ -51,6 +52,17 @@ runs: XCODE_VERSION=$LATEST_XCODE_VERSION ;; minimum) XCODE_VERSION=$MINIMUM_XCODE_VERSION ;; + beta) + # Try to auto-detect installed Xcode 26 beta app name + DETECTED=$(ls -1 /Applications 2>/dev/null | grep -E '^Xcode_26.*\.app$' | head -n1 || true) + if [ -n "$DETECTED" ]; then + # strip prefix and suffix to get the version token used in the path template + # e.g., Xcode_26.0_beta.app -> 26.0_beta + XCODE_VERSION=$(echo "$DETECTED" | sed -E 's/^Xcode_//; s/\.app$//') + else + XCODE_VERSION=$DEFAULT_BETA_XCODE_VERSION + fi + ;; *) XCODE_VERSION=$INPUT_XCODE_VERSION ;; esac @@ -66,36 +78,68 @@ runs: case $INPUT_PLATFORM/$INPUT_XCODE_VERSION in iOS/latest) + DEVICE="iPhone 16 Pro Max" + OS_VERSION="18.5" + ;; + iOS/beta) DEVICE="iPhone 16" - OS_VERSION="18.2" + OS_VERSION="26.0" + ;; + iOS/minimum) + DEVICE="iPhone 16 Pro Max" + OS_VERSION="18.0" ;; iOS/*) - DEVICE="iPhone 15" - OS_VERSION="17.0.1" + DEVICE="iPhone 16 Pro Max" + OS_VERSION="18.5" ;; tvOS/latest) DEVICE="Apple TV 4K (3rd generation)" - OS_VERSION="18.2" + OS_VERSION="18.5" + ;; + tvOS/beta) + DEVICE="Apple TV 4K (3rd generation)" + OS_VERSION="26.0" + ;; + tvOS/minimum) + DEVICE="Apple TV 4K (3rd generation)" + OS_VERSION="18.0" ;; tvOS/*) DEVICE="Apple TV 4K (3rd generation)" - OS_VERSION="17.0" + OS_VERSION="18.5" ;; watchOS/latest) DEVICE="Apple Watch Series 10 (46mm)" - OS_VERSION="11.2" + OS_VERSION="11.5" + ;; + watchOS/beta) + DEVICE="Apple Watch Series 10 (46mm)" + OS_VERSION="26.0" + ;; + watchOS/minimum) + DEVICE="Apple Watch SE (44mm) (2nd generation)" + OS_VERSION="11.0" ;; watchOS/*) - DEVICE="Apple Watch Series 7 (45mm)" - OS_VERSION="10.0" + DEVICE="iPhone 16 Pro Max" + OS_VERSION="18.5" ;; visionOS/latest) DEVICE="Apple Vision Pro" - OS_VERSION="2.2" + OS_VERSION="2.5" + ;; + visionOS/beta) + DEVICE="Apple Vision Pro" + OS_VERSION="26.0" + ;; + visionOS/minimum) + DEVICE="Apple Vision Pro" + OS_VERSION="2.0" ;; visionOS/*) DEVICE="Apple Vision Pro" - OS_VERSION="1.0" + OS_VERSION="2.5" ;; esac diff --git a/.github/composite_actions/install_simulators_if_needed/action.yml b/.github/composite_actions/install_simulators_if_needed/action.yml new file mode 100644 index 0000000000..c8af3347b4 --- /dev/null +++ b/.github/composite_actions/install_simulators_if_needed/action.yml @@ -0,0 +1,60 @@ +name: 'Install Simulators if Needed' +description: 'Downloads and installs simulator runtimes for Xcode 16.0' + +inputs: + xcode_version: + description: 'The Xcode version being used' + required: true + type: string + platform: + description: 'The platform to install simulators for' + required: true + type: string + +runs: + using: "composite" + steps: + - name: Install Simulators for Xcode 16.0 + shell: bash + run: | + XCODE_VERSION="${{ inputs.xcode_version }}" + PLATFORM="${{ inputs.platform }}" + + # Only run for Xcode 16.0.0 + if [[ "$XCODE_VERSION" != "16.0.0" ]]; then + echo "Not Xcode 16.0.0 (current: $XCODE_VERSION), skipping simulator installation" + exit 0 + fi + + # Skip for macOS as it doesn't need simulators + if [[ "$PLATFORM" == "macOS" ]]; then + echo "macOS doesn't need simulator downloads" + exit 0 + fi + + echo "Installing simulators for $PLATFORM with Xcode 16.0.0..." + + # Show what's available before download + echo "Simulators before download:" + xcrun simctl list runtimes || true + + # Download the platform - this will get the appropriate version for Xcode 16.0 + echo "Downloading $PLATFORM platform for Xcode 16.0..." + case $PLATFORM in + iOS) + sudo xcodebuild -downloadPlatform iOS || echo "Failed to download iOS platform" + ;; + tvOS) + sudo xcodebuild -downloadPlatform tvOS || echo "Failed to download tvOS platform" + ;; + watchOS) + sudo xcodebuild -downloadPlatform watchOS || echo "Failed to download watchOS platform" + ;; + visionOS) + sudo xcodebuild -downloadPlatform visionOS || echo "Failed to download visionOS platform" + ;; + esac + + # Show what's available after download + echo "Simulators after download:" + xcrun simctl list runtimes || true \ No newline at end of file diff --git a/.github/workflows/api-breaking-changes-detection.yml b/.github/workflows/api-breaking-changes-detection.yml index e245f17866..e55023d9cf 100644 --- a/.github/workflows/api-breaking-changes-detection.yml +++ b/.github/workflows/api-breaking-changes-detection.yml @@ -10,7 +10,7 @@ permissions: jobs: build-and-check-api-breakage: name: Build and Check API Breakage - runs-on: macos-latest + runs-on: macos-15 steps: - name: Checkout repository diff --git a/.github/workflows/api_digester_check.yml b/.github/workflows/api_digester_check.yml index 4eaece17cf..0b82af5372 100644 --- a/.github/workflows/api_digester_check.yml +++ b/.github/workflows/api_digester_check.yml @@ -7,7 +7,7 @@ on: jobs: check-swift-api-digester: - runs-on: macos-latest + runs-on: macos-15 steps: - name: Checkout repository diff --git a/.github/workflows/build_minimum_supported_swift_platforms.yml b/.github/workflows/build_minimum_supported_swift_platforms.yml index 7f6052abfc..cde24c61cb 100644 --- a/.github/workflows/build_minimum_supported_swift_platforms.yml +++ b/.github/workflows/build_minimum_supported_swift_platforms.yml @@ -27,7 +27,7 @@ jobs: uses: ./.github/workflows/build_scheme.yml with: scheme: Amplify-Build - os-runner: 'macos-latest' + os-runner: 'macos-15' xcode-version: 'minimum' platform: ${{ matrix.platform }} save_build_cache: false diff --git a/.github/workflows/build_scheme.yml b/.github/workflows/build_scheme.yml index 4c66363d65..787a993a17 100644 --- a/.github/workflows/build_scheme.yml +++ b/.github/workflows/build_scheme.yml @@ -43,6 +43,12 @@ jobs: platform: ${{ inputs.platform }} xcode_version: ${{ inputs.xcode-version }} + - name: Install simulators if needed + uses: ./.github/composite_actions/install_simulators_if_needed + with: + xcode_version: ${{ steps.platform.outputs.xcode-version }} + platform: ${{ inputs.platform }} + - name: Attempt to use the dependencies cache id: dependencies-cache timeout-minutes: 4 diff --git a/.github/workflows/build_xcode_beta.yml b/.github/workflows/build_xcode_beta.yml new file mode 100644 index 0000000000..19ce18820e --- /dev/null +++ b/.github/workflows/build_xcode_beta.yml @@ -0,0 +1,44 @@ +name: Build with Xcode Beta | Amplify Swift + +on: + workflow_dispatch: + pull_request: + branches: + - main + push: + branches: + - main + +permissions: + contents: read + actions: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.ref_name != 'main' }} + +jobs: + build-amplify-with-xcode-beta: + name: Build Amplify Swift for ${{ matrix.platform }} + strategy: + fail-fast: false + matrix: + platform: [macOS] + + uses: ./.github/workflows/build_scheme.yml + with: + scheme: Amplify-Build + os-runner: 'macos-15' + xcode-version: 'beta' + platform: ${{ matrix.platform }} + save_build_cache: false + + confirm-pass: + runs-on: ubuntu-latest + name: Confirm Passing Build Steps + if: ${{ !cancelled() }} + needs: [build-amplify-with-xcode-beta] + env: + EXIT_CODE: ${{ contains(needs.*.result, 'failure') && 1 || 0 }} + steps: + - run: exit $EXIT_CODE diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index 1de1725fd1..8bbbca62f2 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -1,6 +1,7 @@ name: Canary Test on: + workflow_dispatch: schedule: - cron: '0 16 * * *' # Everyday 16:00 UTC @@ -15,14 +16,14 @@ jobs: strategy: matrix: include: - - os: macos-latest - xcode-version: 15.3.0 - device: iPhone 15 - version: 17.4 - - os: macos-latest - xcode-version: 15.0.1 - device: iPhone 14 - version: 17.0.1 + - os: macos-15 + xcode-version: '16.4.0' + device: 'iPhone 16 Pro Max' + version: '18.5' + - os: macos-15 + xcode-version: '16.0.0' + device: 'iPhone 16 Pro Max' + version: '18.0' name: Canary Test - Xcode ${{ matrix.xcode-version }} runs-on: ${{ matrix.os }} steps: @@ -50,6 +51,12 @@ jobs: sudo xcode-select -s "/Applications/Xcode_${{ matrix.xcode-version }}.app" xcodebuild -version + - name: Install simulators if needed + uses: ./.github/composite_actions/install_simulators_if_needed + with: + xcode_version: ${{ matrix.xcode-version }} + platform: iOS + - name: Run Tests - ${{ matrix.device }} with iOS ${{ matrix.version }} working-directory: ${{ github.workspace }}/canaries/example run: bundle exec fastlane scan --device "${{ matrix.device }}" --deployment_target_version "${{ matrix.version }}" diff --git a/.github/workflows/release_kickoff.yml b/.github/workflows/release_kickoff.yml index 76fc248335..4617e5f4fc 100644 --- a/.github/workflows/release_kickoff.yml +++ b/.github/workflows/release_kickoff.yml @@ -10,7 +10,7 @@ permissions: jobs: release: name: Release - runs-on: macos-latest + runs-on: ubuntu-latest steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1 diff --git a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/index.mjs b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/index.mjs index 7283e7c862..cf8f0eb090 100644 --- a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/index.mjs +++ b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/LocalServer/index.mjs @@ -46,7 +46,7 @@ app.post("/notifications", async (req, res) => { data: data ?? {} } try { - const cmd = `echo '${JSON.stringify(apns)}' | xcrun simctl --set testing push ${deviceId} ${bundleId} -` + const cmd = `echo '${JSON.stringify(apns)}' | xcrun simctl push ${deviceId} ${bundleId} -` await run(cmd) res.send("Done") } catch (error) { @@ -60,7 +60,7 @@ app.post('/uninstall', async (req, res) => { console.log("POST /uninstall ") const { deviceId } = req.body try { - const cmd = `xcrun simctl --set testing uninstall ${deviceId} ${bundleId}` + const cmd = `xcrun simctl uninstall ${deviceId} ${bundleId}` await run(cmd) res.send("Done") } catch (error) { @@ -73,7 +73,7 @@ app.post('/boot', async (req, res) => { console.log("POST /boot ") const { deviceId } = req.body try { - const cmd = `xcrun simctl --set testing bootstatus ${deviceId} -b` + const cmd = `xcrun simctl bootstatus ${deviceId} -b` await run(cmd) res.send("Done") } catch (error) { @@ -84,4 +84,4 @@ app.post('/boot', async (req, res) => { app.listen(9293, () => { console.log("Starting server") -}) +}) \ No newline at end of file diff --git a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationGen2HostApp.xctestplan b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationGen2HostApp.xctestplan index 8a1c6e72f2..4f89e013f5 100644 --- a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationGen2HostApp.xctestplan +++ b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationGen2HostApp.xctestplan @@ -23,7 +23,6 @@ }, "testTargets" : [ { - "parallelizable" : true, "target" : { "containerPath" : "container:PushNotificationHostApp.xcodeproj", "identifier" : "6084F1AB2967B87200434CBF", diff --git a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostApp.xcodeproj/xcshareddata/xcschemes/PushNotificationHostApp.xcscheme b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostApp.xcodeproj/xcshareddata/xcschemes/PushNotificationHostApp.xcscheme index 4aa810bccc..d1631e02d1 100644 --- a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostApp.xcodeproj/xcshareddata/xcschemes/PushNotificationHostApp.xcscheme +++ b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationHostApp.xcodeproj/xcshareddata/xcschemes/PushNotificationHostApp.xcscheme @@ -31,7 +31,7 @@ + parallelizable = "NO"> + parallelizable = "NO"> = 30 && + component.count <= 50 && + component.allSatisfy({ $0.isLetter || $0.isNumber || $0 == "-" }) { + return component + } + } + + // Last resort: try to extract from the full path using regex + let uuidPattern = "[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}" + if let regex = try? NSRegularExpression(pattern: uuidPattern), + let match = regex.firstMatch(in: bundlePath, range: NSRange(bundlePath.startIndex..., in: bundlePath)), + let range = Range(match.range, in: bundlePath) { + return String(bundlePath[range]) + } + + // If all else fails, provide detailed error information + fatalError("Could not extract device identifier from bundle path: \(bundlePath)\nPath components: \(paths)") }() @MainActor @@ -131,7 +154,7 @@ final class PushNotificationHostAppUITests: XCTestCase { journey: nil, deeplink: nil )), - deviceId: deviceIdentifier! + deviceId: deviceIdentifier )) let notification = notificationElement() @@ -169,7 +192,7 @@ final class PushNotificationHostAppUITests: XCTestCase { body: #function ), data: nil, - deviceId: deviceIdentifier! + deviceId: deviceIdentifier )) let notification = notificationElement() @@ -212,7 +235,7 @@ final class PushNotificationHostAppUITests: XCTestCase { journey: nil, deeplink: nil )), - deviceId: deviceIdentifier! + deviceId: deviceIdentifier )) let expectedEvent = anyElementContains(text: "Amplify.BasicAnalyticsEvent(name: \"_campaign.received_", scope: app) @@ -234,7 +257,7 @@ final class PushNotificationHostAppUITests: XCTestCase { body: #function ), data: nil, - deviceId: deviceIdentifier! + deviceId: deviceIdentifier )) let unexpectedEvent = anyElementContains(text: "Amplify.BasicAnalyticsEvent(name: \"_campaign.received_", scope: app) @@ -301,13 +324,13 @@ final class PushNotificationHostAppUITests: XCTestCase { } private func uninstallApp() async throws { - let request = LocalServer.uninstall(deviceIdentifier!).urlRequest + let request = LocalServer.uninstall(deviceIdentifier).urlRequest let (_, response) = try await URLSession.shared.data(for: request) XCTAssertTrue((response as! HTTPURLResponse).statusCode < 300, "Failed to uninstall the App") } private func bootDevice() async throws { - let request = LocalServer.boot(deviceIdentifier!).urlRequest + let request = LocalServer.boot(deviceIdentifier).urlRequest let (_, response) = try await URLSession.shared.data(for: request) XCTAssertTrue((response as! HTTPURLResponse).statusCode < 300, "Failed to boot the device") } diff --git a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationWatchTests.xctestplan b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationWatchTests.xctestplan index 8c24292cf1..5d791ae127 100644 --- a/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationWatchTests.xctestplan +++ b/AmplifyPlugins/Notifications/Push/Tests/PushNotificationHostApp/PushNotificationWatchTests.xctestplan @@ -13,7 +13,6 @@ }, "testTargets" : [ { - "parallelizable" : true, "target" : { "containerPath" : "container:PushNotificationHostApp.xcodeproj", "identifier" : "6875F9882A3CD258001C9AAF",