From 29cc4b83c0242cd991f049bfed7b198dd7b0f35c Mon Sep 17 00:00:00 2001 From: busec0 Date: Mon, 27 Apr 2026 09:22:16 +0300 Subject: [PATCH 1/5] add version pinning --- .github/workflows/dotnet-maui-ci.yml | 21 ++++++++++++------- .../DittoMauiTasksApp/workload-rollback.json | 6 ++++++ dotnet-maui/global.json | 6 ++++++ 3 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 dotnet-maui/DittoMauiTasksApp/workload-rollback.json create mode 100644 dotnet-maui/global.json diff --git a/.github/workflows/dotnet-maui-ci.yml b/.github/workflows/dotnet-maui-ci.yml index 1a889e356..8a80bf203 100644 --- a/.github/workflows/dotnet-maui-ci.yml +++ b/.github/workflows/dotnet-maui-ci.yml @@ -24,7 +24,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: "9.0.x" + global-json-file: dotnet-maui/global.json - name: Create test .env file run: | @@ -36,7 +36,7 @@ jobs: - name: Restore workloads and dependencies working-directory: dotnet-maui/DittoMauiTasksApp run: | - dotnet workload restore + dotnet workload restore --from-rollback-file workload-rollback.json dotnet restore - name: Run .NET format check @@ -61,7 +61,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: "9.0.x" + global-json-file: dotnet-maui/global.json - name: Setup Android SDK uses: android-actions/setup-android@v3 @@ -76,7 +76,7 @@ jobs: - name: Restore workloads and dependencies working-directory: dotnet-maui/DittoMauiTasksApp run: | - dotnet workload restore + dotnet workload restore --from-rollback-file workload-rollback.json dotnet restore - name: Build Android APK (unsigned Release) @@ -101,10 +101,15 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Setup Xcode + uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: "26.2" + - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: "9.0.x" + global-json-file: dotnet-maui/global.json - name: Create .env file run: | @@ -116,7 +121,7 @@ jobs: - name: Restore workloads and dependencies working-directory: dotnet-maui/DittoMauiTasksApp run: | - dotnet workload restore + dotnet workload restore --from-rollback-file workload-rollback.json dotnet restore --runtime ios-arm64 - name: Build iOS Device (Release for BrowserStack) @@ -159,7 +164,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: "9.0.x" + global-json-file: dotnet-maui/global.json - name: Download Android APK artifact uses: actions/download-artifact@v4 @@ -285,7 +290,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: "9.0.x" + global-json-file: dotnet-maui/global.json - name: Download iOS IPA artifact uses: actions/download-artifact@v4 diff --git a/dotnet-maui/DittoMauiTasksApp/workload-rollback.json b/dotnet-maui/DittoMauiTasksApp/workload-rollback.json new file mode 100644 index 000000000..d5eaf1350 --- /dev/null +++ b/dotnet-maui/DittoMauiTasksApp/workload-rollback.json @@ -0,0 +1,6 @@ +{ + "microsoft.net.sdk.ios": "18.2.9173/9.0.100", + "microsoft.net.sdk.maccatalyst": "18.2.9173/9.0.100", + "microsoft.net.sdk.android": "35.0.39/9.0.100", + "microsoft.net.sdk.maui": "9.0.14/9.0.100" +} diff --git a/dotnet-maui/global.json b/dotnet-maui/global.json new file mode 100644 index 000000000..9cbe59bac --- /dev/null +++ b/dotnet-maui/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "9.0.102", + "rollForward": "latestFeature" + } +} From 046d0efa742993be7d55f969678829178b961d74 Mon Sep 17 00:00:00 2001 From: busec0 Date: Mon, 27 Apr 2026 09:30:50 +0300 Subject: [PATCH 2/5] fix: split workload update and restore to allow rollback file dotnet workload restore --from-rollback-file fails because the command internally invokes install with --skip-manifest-update, which conflicts with --from-rollback-file. Use update first to pin manifest versions, then plain restore to install workloads using those pins. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/dotnet-maui-ci.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dotnet-maui-ci.yml b/.github/workflows/dotnet-maui-ci.yml index 8a80bf203..c9bdf8885 100644 --- a/.github/workflows/dotnet-maui-ci.yml +++ b/.github/workflows/dotnet-maui-ci.yml @@ -36,7 +36,8 @@ jobs: - name: Restore workloads and dependencies working-directory: dotnet-maui/DittoMauiTasksApp run: | - dotnet workload restore --from-rollback-file workload-rollback.json + dotnet workload update --from-rollback-file workload-rollback.json + dotnet workload restore dotnet restore - name: Run .NET format check @@ -76,7 +77,8 @@ jobs: - name: Restore workloads and dependencies working-directory: dotnet-maui/DittoMauiTasksApp run: | - dotnet workload restore --from-rollback-file workload-rollback.json + dotnet workload update --from-rollback-file workload-rollback.json + dotnet workload restore dotnet restore - name: Build Android APK (unsigned Release) @@ -121,7 +123,8 @@ jobs: - name: Restore workloads and dependencies working-directory: dotnet-maui/DittoMauiTasksApp run: | - dotnet workload restore --from-rollback-file workload-rollback.json + dotnet workload update --from-rollback-file workload-rollback.json + dotnet workload restore dotnet restore --runtime ios-arm64 - name: Build iOS Device (Release for BrowserStack) From 8e381644169ffb6c0d1f99560bb5bf87308a28b8 Mon Sep 17 00:00:00 2001 From: busec0 Date: Mon, 27 Apr 2026 09:45:30 +0300 Subject: [PATCH 3/5] fix: pin iOS/maccatalyst TargetPlatformVersion to 18.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The workload manifest pin alone wasn't enough — the macos-26 runner has the iOS 26.2 SDK pack pre-installed, so MSBuild resolved net9.0-ios to the highest available TPV (26.2.9008), which requires Xcode 26.3. Pinning the TPV explicitly (net9.0-ios18.2, net9.0-maccatalyst18.2) forces MSBuild to use the 18.2 SDK pack regardless of what newer packs are installed on the runner. Updates the workflow build/output paths, UITests path lookup, and README examples to match. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/dotnet-maui-ci.yml | 6 +++--- dotnet-maui/DittoMauiTasksApp/DittoMauiTasksApp.csproj | 4 ++-- dotnet-maui/README.md | 4 ++-- dotnet-maui/UITests.iOS/iOSTaskSearchTests.cs | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/dotnet-maui-ci.yml b/.github/workflows/dotnet-maui-ci.yml index c9bdf8885..3491bb39a 100644 --- a/.github/workflows/dotnet-maui-ci.yml +++ b/.github/workflows/dotnet-maui-ci.yml @@ -130,13 +130,13 @@ jobs: - name: Build iOS Device (Release for BrowserStack) working-directory: dotnet-maui/DittoMauiTasksApp run: | - dotnet build DittoMauiTasksApp.csproj --configuration Release --framework net9.0-ios --no-restore \ + dotnet build DittoMauiTasksApp.csproj --configuration Release --framework net9.0-ios18.2 --no-restore \ /p:RuntimeIdentifier=ios-arm64 \ /p:EnableCodeSigning=false \ /p:ArchiveOnBuild=false - name: Create iOS IPA for BrowserStack - working-directory: dotnet-maui/DittoMauiTasksApp/bin/Release/net9.0-ios/ios-arm64 + working-directory: dotnet-maui/DittoMauiTasksApp/bin/Release/net9.0-ios18.2/ios-arm64 run: | mkdir -p Payload cp -r DittoMauiTasksApp.app Payload/ @@ -148,7 +148,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: ios-ipa - path: dotnet-maui/DittoMauiTasksApp/bin/Release/net9.0-ios/ios-arm64/DittoMauiTasksApp-device.ipa + path: dotnet-maui/DittoMauiTasksApp/bin/Release/net9.0-ios18.2/ios-arm64/DittoMauiTasksApp-device.ipa retention-days: 1 browserstack-android: diff --git a/dotnet-maui/DittoMauiTasksApp/DittoMauiTasksApp.csproj b/dotnet-maui/DittoMauiTasksApp/DittoMauiTasksApp.csproj index 5dfb2ed04..f94d8ce9a 100644 --- a/dotnet-maui/DittoMauiTasksApp/DittoMauiTasksApp.csproj +++ b/dotnet-maui/DittoMauiTasksApp/DittoMauiTasksApp.csproj @@ -1,8 +1,8 @@  - net9.0-android;net9.0-ios;net9.0-maccatalyst - net9.0-windows10.0.19041.0;net9.0-android;net9.0-ios;net9.0-maccatalyst + net9.0-android;net9.0-ios18.2;net9.0-maccatalyst18.2 + net9.0-windows10.0.19041.0;net9.0-android;net9.0-ios18.2;net9.0-maccatalyst18.2 net9.0-android Exe DittoMauiTasksApp diff --git a/dotnet-maui/README.md b/dotnet-maui/README.md index 329afefb3..cfb1eb428 100644 --- a/dotnet-maui/README.md +++ b/dotnet-maui/README.md @@ -26,7 +26,7 @@ dotnet restore These commands will build and run the app on the default iOS target: ```sh -dotnet build -t:Run -f net9.0-ios +dotnet build -t:Run -f net9.0-ios18.2 ``` ### Building and Running the App on Android @@ -40,7 +40,7 @@ dotnet build -t:Run -f net9.0-android ### Building and Running the App on MacOS ```sh -dotnet build -t:Run -f net9.0-maccatalyst +dotnet build -t:Run -f net9.0-maccatalyst18.2 ``` ### Building and Running the App on Windows diff --git a/dotnet-maui/UITests.iOS/iOSTaskSearchTests.cs b/dotnet-maui/UITests.iOS/iOSTaskSearchTests.cs index 0343360d7..6c08429bb 100644 --- a/dotnet-maui/UITests.iOS/iOSTaskSearchTests.cs +++ b/dotnet-maui/UITests.iOS/iOSTaskSearchTests.cs @@ -65,12 +65,12 @@ private string GetAppPath() { // Look for the .app in the MAUI project's build output var projectRoot = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "..")); - var appPath = Path.Combine(projectRoot, "DittoMauiTasksApp", "bin", "Debug", "net9.0-ios", "iossimulator-arm64"); + var appPath = Path.Combine(projectRoot, "DittoMauiTasksApp", "bin", "Debug", "net9.0-ios18.2", "iossimulator-arm64"); if (!Directory.Exists(appPath)) { // Fallback to x64 simulator - appPath = Path.Combine(projectRoot, "DittoMauiTasksApp", "bin", "Debug", "net9.0-ios", "iossimulator-x64"); + appPath = Path.Combine(projectRoot, "DittoMauiTasksApp", "bin", "Debug", "net9.0-ios18.2", "iossimulator-x64"); } return appPath; From 8e7f59b83a0e96492660377433b261c9463db4a5 Mon Sep 17 00:00:00 2001 From: Bulzan Sergiu Date: Mon, 27 Apr 2026 09:48:40 +0300 Subject: [PATCH 4/5] Update dotnet-maui/global.json Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dotnet-maui/global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet-maui/global.json b/dotnet-maui/global.json index 9cbe59bac..a7035aa8b 100644 --- a/dotnet-maui/global.json +++ b/dotnet-maui/global.json @@ -1,6 +1,6 @@ { "sdk": { "version": "9.0.102", - "rollForward": "latestFeature" + "rollForward": "latestPatch" } } From 7a268acdf1e00fe48ea4013d4b7aac710ff59019 Mon Sep 17 00:00:00 2001 From: busec0 Date: Mon, 27 Apr 2026 10:00:25 +0300 Subject: [PATCH 5/5] revert workload pin, use latest .NET iOS workload with Xcode 26.3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The workload manifest pin (18.2.9173) didn't expose a usable TPV path on the macos-26 runner — NETSDK1140 listed only 18.0 and 26.2 as valid. Rather than fight pack resolution, take the simpler route: use the latest workload and pin Xcode to 26.3 (which is what 26.2.9008 needs). Keeps the SDK pin in dotnet-maui/global.json and the explicit Xcode selection in CI so the "out of sync Xcode/.NET iOS" failure mode is still controlled, just from the Xcode side. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/dotnet-maui-ci.yml | 11 ++++------- .../DittoMauiTasksApp/DittoMauiTasksApp.csproj | 4 ++-- dotnet-maui/DittoMauiTasksApp/workload-rollback.json | 6 ------ dotnet-maui/README.md | 4 ++-- dotnet-maui/UITests.iOS/iOSTaskSearchTests.cs | 4 ++-- 5 files changed, 10 insertions(+), 19 deletions(-) delete mode 100644 dotnet-maui/DittoMauiTasksApp/workload-rollback.json diff --git a/.github/workflows/dotnet-maui-ci.yml b/.github/workflows/dotnet-maui-ci.yml index 3491bb39a..626a8cc1f 100644 --- a/.github/workflows/dotnet-maui-ci.yml +++ b/.github/workflows/dotnet-maui-ci.yml @@ -36,7 +36,6 @@ jobs: - name: Restore workloads and dependencies working-directory: dotnet-maui/DittoMauiTasksApp run: | - dotnet workload update --from-rollback-file workload-rollback.json dotnet workload restore dotnet restore @@ -77,7 +76,6 @@ jobs: - name: Restore workloads and dependencies working-directory: dotnet-maui/DittoMauiTasksApp run: | - dotnet workload update --from-rollback-file workload-rollback.json dotnet workload restore dotnet restore @@ -106,7 +104,7 @@ jobs: - name: Setup Xcode uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: "26.2" + xcode-version: "26.3" - name: Setup .NET uses: actions/setup-dotnet@v4 @@ -123,20 +121,19 @@ jobs: - name: Restore workloads and dependencies working-directory: dotnet-maui/DittoMauiTasksApp run: | - dotnet workload update --from-rollback-file workload-rollback.json dotnet workload restore dotnet restore --runtime ios-arm64 - name: Build iOS Device (Release for BrowserStack) working-directory: dotnet-maui/DittoMauiTasksApp run: | - dotnet build DittoMauiTasksApp.csproj --configuration Release --framework net9.0-ios18.2 --no-restore \ + dotnet build DittoMauiTasksApp.csproj --configuration Release --framework net9.0-ios --no-restore \ /p:RuntimeIdentifier=ios-arm64 \ /p:EnableCodeSigning=false \ /p:ArchiveOnBuild=false - name: Create iOS IPA for BrowserStack - working-directory: dotnet-maui/DittoMauiTasksApp/bin/Release/net9.0-ios18.2/ios-arm64 + working-directory: dotnet-maui/DittoMauiTasksApp/bin/Release/net9.0-ios/ios-arm64 run: | mkdir -p Payload cp -r DittoMauiTasksApp.app Payload/ @@ -148,7 +145,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: ios-ipa - path: dotnet-maui/DittoMauiTasksApp/bin/Release/net9.0-ios18.2/ios-arm64/DittoMauiTasksApp-device.ipa + path: dotnet-maui/DittoMauiTasksApp/bin/Release/net9.0-ios/ios-arm64/DittoMauiTasksApp-device.ipa retention-days: 1 browserstack-android: diff --git a/dotnet-maui/DittoMauiTasksApp/DittoMauiTasksApp.csproj b/dotnet-maui/DittoMauiTasksApp/DittoMauiTasksApp.csproj index f94d8ce9a..5dfb2ed04 100644 --- a/dotnet-maui/DittoMauiTasksApp/DittoMauiTasksApp.csproj +++ b/dotnet-maui/DittoMauiTasksApp/DittoMauiTasksApp.csproj @@ -1,8 +1,8 @@  - net9.0-android;net9.0-ios18.2;net9.0-maccatalyst18.2 - net9.0-windows10.0.19041.0;net9.0-android;net9.0-ios18.2;net9.0-maccatalyst18.2 + net9.0-android;net9.0-ios;net9.0-maccatalyst + net9.0-windows10.0.19041.0;net9.0-android;net9.0-ios;net9.0-maccatalyst net9.0-android Exe DittoMauiTasksApp diff --git a/dotnet-maui/DittoMauiTasksApp/workload-rollback.json b/dotnet-maui/DittoMauiTasksApp/workload-rollback.json deleted file mode 100644 index d5eaf1350..000000000 --- a/dotnet-maui/DittoMauiTasksApp/workload-rollback.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "microsoft.net.sdk.ios": "18.2.9173/9.0.100", - "microsoft.net.sdk.maccatalyst": "18.2.9173/9.0.100", - "microsoft.net.sdk.android": "35.0.39/9.0.100", - "microsoft.net.sdk.maui": "9.0.14/9.0.100" -} diff --git a/dotnet-maui/README.md b/dotnet-maui/README.md index cfb1eb428..329afefb3 100644 --- a/dotnet-maui/README.md +++ b/dotnet-maui/README.md @@ -26,7 +26,7 @@ dotnet restore These commands will build and run the app on the default iOS target: ```sh -dotnet build -t:Run -f net9.0-ios18.2 +dotnet build -t:Run -f net9.0-ios ``` ### Building and Running the App on Android @@ -40,7 +40,7 @@ dotnet build -t:Run -f net9.0-android ### Building and Running the App on MacOS ```sh -dotnet build -t:Run -f net9.0-maccatalyst18.2 +dotnet build -t:Run -f net9.0-maccatalyst ``` ### Building and Running the App on Windows diff --git a/dotnet-maui/UITests.iOS/iOSTaskSearchTests.cs b/dotnet-maui/UITests.iOS/iOSTaskSearchTests.cs index 6c08429bb..0343360d7 100644 --- a/dotnet-maui/UITests.iOS/iOSTaskSearchTests.cs +++ b/dotnet-maui/UITests.iOS/iOSTaskSearchTests.cs @@ -65,12 +65,12 @@ private string GetAppPath() { // Look for the .app in the MAUI project's build output var projectRoot = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "..")); - var appPath = Path.Combine(projectRoot, "DittoMauiTasksApp", "bin", "Debug", "net9.0-ios18.2", "iossimulator-arm64"); + var appPath = Path.Combine(projectRoot, "DittoMauiTasksApp", "bin", "Debug", "net9.0-ios", "iossimulator-arm64"); if (!Directory.Exists(appPath)) { // Fallback to x64 simulator - appPath = Path.Combine(projectRoot, "DittoMauiTasksApp", "bin", "Debug", "net9.0-ios18.2", "iossimulator-x64"); + appPath = Path.Combine(projectRoot, "DittoMauiTasksApp", "bin", "Debug", "net9.0-ios", "iossimulator-x64"); } return appPath;