diff --git a/.github/workflows/ci-acp-sdk.yml b/.github/workflows/ci-acp-sdk.yml
index 00a0fe50..d5681fd2 100644
--- a/.github/workflows/ci-acp-sdk.yml
+++ b/.github/workflows/ci-acp-sdk.yml
@@ -64,18 +64,18 @@ jobs:
timeout-minutes: 15
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
- name: Run the pinned upstream Agent against this SDK
run: bash scripts/gates/run-acp-official-v2-agent-gate.sh
- name: Upload upstream interoperability evidence
if: always()
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: acp-official-v2-agent
path: |
@@ -94,18 +94,18 @@ jobs:
MSBUILDDISABLENODEREUSE: "1"
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
- name: Verify real stdio batches without skips
run: bash scripts/gates/run-acp-batch-stdio-gate.sh Release
- name: Upload stdio evidence
if: always()
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: acp-batch-stdio
path: artifacts/acp-batch-stdio/
@@ -119,12 +119,12 @@ jobs:
# history (tags included) must be present. A shallow clone would pack a 0.0.0-alpha version
# and the baseline guard in SalmonEgg.Acp.csproj would fail the pack.
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
@@ -141,7 +141,7 @@ jobs:
run: ./scripts/gates/run-acp-sdk-tag-version-gate-selftest.sh
- name: Upload ACP SDK package
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: acp-sdk-package
path: "${{ env.PACKAGE_OUTPUT }}/*.nupkg"
@@ -153,15 +153,15 @@ jobs:
timeout-minutes: 25
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
- name: Download ACP SDK package
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: acp-sdk-package
path: artifacts/acp-sdk-package
diff --git a/.github/workflows/ci-core.yml b/.github/workflows/ci-core.yml
index bde1efbb..7b74dfef 100644
--- a/.github/workflows/ci-core.yml
+++ b/.github/workflows/ci-core.yml
@@ -53,11 +53,11 @@ jobs:
MSBUILDDISABLENODEREUSE: "1"
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
- name: Verify real cancellation transports without skips
@@ -68,11 +68,11 @@ jobs:
shell: bash
run: |
python3 -m venv "$RUNNER_TEMP/acp-bridge-venv"
- "$RUNNER_TEMP/acp-bridge-venv/bin/pip" install acpremote==1.9.0
+ "$RUNNER_TEMP/acp-bridge-venv/bin/pip" install acpremote==1.10.0
python3 scripts/gates/run-acp-published-bridge-gate.py --bridge "$RUNNER_TEMP/acp-bridge-venv/bin/acpremote"
- name: Upload transport evidence
if: always()
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: acp-cancellation-transports
path: |
@@ -86,12 +86,12 @@ jobs:
timeout-minutes: 45
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
@@ -173,14 +173,14 @@ jobs:
- name: Upload test results
if: always()
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: test-results
path: "${{ env.TEST_RESULTS_DIR }}/**/*.trx"
- name: Upload coverage reports
if: always()
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: coverage-report
path: "**/coverage*.xml"
@@ -196,7 +196,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 1
persist-credentials: false
diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml
index 5a39272b..ad083cf3 100644
--- a/.github/workflows/code-quality.yml
+++ b/.github/workflows/code-quality.yml
@@ -48,12 +48,12 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 70b6e82f..d75f60a7 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -52,15 +52,15 @@ jobs:
contents: read
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
- name: Initialize CodeQL
- uses: github/codeql-action/init@42947a340483f03ba47bb1a039b2c519aab3df85 # v3.37.8
+ uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
with:
languages: csharp
build-mode: manual
@@ -97,6 +97,6 @@ jobs:
done
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@42947a340483f03ba47bb1a039b2c519aab3df85 # v3.37.8
+ uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
with:
category: "/language:csharp"
diff --git a/.github/workflows/gui-smoke-gates.yml b/.github/workflows/gui-smoke-gates.yml
index b45eb1d2..874fc493 100644
--- a/.github/workflows/gui-smoke-gates.yml
+++ b/.github/workflows/gui-smoke-gates.yml
@@ -49,10 +49,10 @@ jobs:
timeout-minutes: 45
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
@@ -79,7 +79,7 @@ jobs:
- name: Upload ACP setup path evidence
if: always()
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: acp-setup-path-smoke
path: ${{ runner.temp }}/acp-setup-path-smoke
@@ -97,7 +97,7 @@ jobs:
- name: Upload native elicitation evidence
if: always()
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: native-elicitation-consent
path: |
@@ -135,10 +135,10 @@ jobs:
if: github.event_name == 'workflow_dispatch' && vars.WINDOWS_GUI_SELF_HOSTED_RUNNER == 'true'
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
diff --git a/.github/workflows/macos-native-acceptance.yml b/.github/workflows/macos-native-acceptance.yml
index 2566f9da..a8de99f9 100644
--- a/.github/workflows/macos-native-acceptance.yml
+++ b/.github/workflows/macos-native-acceptance.yml
@@ -36,7 +36,7 @@ jobs:
UseSharedCompilation: 'false'
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Verify native observation readiness contracts
@@ -45,7 +45,7 @@ jobs:
timeout-minutes: 3
run: python3 scripts/gates/run-macos-desktop-input-probe.py
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
- name: Build the current native macOS application
@@ -62,7 +62,7 @@ jobs:
run: python3 scripts/gates/macos-elicitation-reverse-verification.py
- name: Upload native desktop evidence
if: always()
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: macos-native-desktop-input
path: |
diff --git a/.github/workflows/native-android-acceptance.yml b/.github/workflows/native-android-acceptance.yml
index da36ade1..75ad6bf8 100644
--- a/.github/workflows/native-android-acceptance.yml
+++ b/.github/workflows/native-android-acceptance.yml
@@ -38,12 +38,12 @@ jobs:
SALMONEGG_ANDROID_REVERSE: ${{ github.event_name == 'workflow_dispatch' && inputs.reverse-verification || false }}
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
@@ -57,7 +57,7 @@ jobs:
'platforms;android-36' 'build-tools;36.0.0' platform-tools emulator 'system-images;android-36;google_apis;x86_64'
echo "$ANDROID_SDK_ROOT/platform-tools" >> "$GITHUB_PATH"
python3 -m venv "$RUNNER_TEMP/android-acp-tools"
- "$RUNNER_TEMP/android-acp-tools/bin/python" -m pip install aiohttp==3.12.15
+ "$RUNNER_TEMP/android-acp-tools/bin/python" -m pip install aiohttp==3.14.4
- name: Build current installable APK
run: |
@@ -133,7 +133,7 @@ jobs:
- name: Upload acceptance evidence
if: always()
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: android-installed-acp-acceptance
path: artifacts/android-acp-acceptance
diff --git a/.github/workflows/native-ios-acceptance.yml b/.github/workflows/native-ios-acceptance.yml
index ddd4a008..20fe1835 100644
--- a/.github/workflows/native-ios-acceptance.yml
+++ b/.github/workflows/native-ios-acceptance.yml
@@ -29,28 +29,28 @@ concurrency:
jobs:
installed-ios:
name: iOS Installed ACP UI
- # Pair the repository's .NET 10 SDK with the published 10.0.300.3 Apple workload set.
- # Xcode 26.5 / iOS 26.5 are both installed on this serviced native host.
+ # Pin the 10.0.401 workload set: its Apple workload (26.5.10318) requires Xcode 26.6, which this GA
+ # image carries. The band's later 10.0.401.1 set moves to Xcode 27, whose hosted image is a preview.
runs-on: macos-26
- timeout-minutes: ${{ github.event_name == 'workflow_dispatch' && inputs.reverse-verification && 65 || 35 }}
+ timeout-minutes: ${{ github.event_name == 'workflow_dispatch' && inputs.reverse-verification && 80 || 35 }}
env:
DOTNET_PROCESSOR_COUNT: 2
DOTNET_CLI_USE_MSBUILD_SERVER: 0
MSBUILDDISABLENODEREUSE: 1
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
- name: Select matching Apple toolchain
run: |
- xcode_developer_dir="$(python3 -c 'import os; print(os.path.realpath("/Applications/Xcode_26.5.app/Contents/Developer"))')"
+ xcode_developer_dir="$(python3 -c 'import os; print(os.path.realpath("/Applications/Xcode_26.6.app/Contents/Developer"))')"
test -d "$xcode_developer_dir"
sudo xcode-select --switch "$xcode_developer_dir"
xcodebuild -version
@@ -58,8 +58,8 @@ jobs:
uname -m
xcrun --sdk iphonesimulator --show-sdk-path
xcrun --sdk macosx --find actool
- # https://github.com/dotnet/macios/releases/tag/dotnet-10.0.1xx-xcode26.5-10284
- dotnet workload install ios --version 10.0.300.3 --disable-parallel --no-http-cache
+ # https://github.com/dotnet/macios/releases/tag/dotnet-10.0.1xx-xcode26.5-10318
+ dotnet workload install ios --version 10.0.401 --disable-parallel --no-http-cache
dotnet workload list
# Wait for CoreSimulatorService before actool / xcodebuild (runner-images #12777).
xcrun simctl list runtimes
@@ -77,7 +77,7 @@ jobs:
- name: Install isolated test tooling
run: |
python3 -m venv "$RUNNER_TEMP/ios-acp-tools"
- "$RUNNER_TEMP/ios-acp-tools/bin/python" -m pip install aiohttp==3.12.15
+ "$RUNNER_TEMP/ios-acp-tools/bin/python" -m pip install aiohttp==3.14.4
curl --location --fail --silent --show-error \
https://github.com/yonaskolb/XcodeGen/releases/download/2.46.0/xcodegen.zip \
--output "$RUNNER_TEMP/xcodegen.zip"
@@ -100,7 +100,7 @@ jobs:
- name: Upload acceptance evidence
if: always()
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: ios-installed-acp-acceptance
path: |
@@ -109,6 +109,7 @@ jobs:
artifacts/ios-acp-acceptance/product-logs/**
artifacts/ios-acp-acceptance/results.xcresult
artifacts/ios-acp-acceptance/test-attachments/**
+ artifacts/ios-acp-acceptance/hang-diagnostics/**
artifacts/ios-acp-acceptance/reverse/*.json
artifacts/ios-acp-acceptance/reverse/*.log
artifacts/ios-acp-acceptance/reverse/red/*.json
diff --git a/.github/workflows/platform-build-gates.yml b/.github/workflows/platform-build-gates.yml
index 9ef87819..96c2e504 100644
--- a/.github/workflows/platform-build-gates.yml
+++ b/.github/workflows/platform-build-gates.yml
@@ -61,14 +61,14 @@ jobs:
timeout-minutes: 45
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# The version identity is derived from git tags by MinVer; a shallow clone sees no tags
# and would stamp 0.0.0 into every manifest and binary.
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
@@ -118,14 +118,14 @@ jobs:
timeout-minutes: 45
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# The version identity is derived from git tags by MinVer; a shallow clone sees no tags
# and would stamp 0.0.0 into every manifest and binary.
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
@@ -189,18 +189,19 @@ jobs:
# packaging chain is not, so this gate builds the package unsigned on every change and asserts the
# package actually carries what its manifest declares.
name: Windows MSIX Package
- runs-on: windows-latest
+ # Match the .NET 10.0.4xx SDK with the Visual Studio 2026 XAML/MSIX toolchain.
+ runs-on: windows-2025-vs2026
timeout-minutes: 60
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# The version identity is derived from git tags by MinVer; a shallow clone sees no tags
# and would stamp 0.0.0 into every manifest and binary.
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
@@ -290,14 +291,14 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# The version identity is derived from git tags by MinVer; a shallow clone sees no tags
# and would stamp 0.0.0 into every manifest and binary.
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
@@ -355,19 +356,20 @@ jobs:
name: iOS Simulator
# Heavy mobile build fast-gated out of standard PRs; runs on push, schedule, and workflow_dispatch.
if: github.event_name != 'pull_request'
- # Pin the OS image because .NET for iOS 26.0 requires Xcode 26.0, while macos-latest can roll forward independently.
- runs-on: macos-15
+ # Pin the 10.0.401 workload set: its Apple workload (26.5.10318) requires Xcode 26.6, which this GA
+ # image carries. The band's later 10.0.401.1 set moves to Xcode 27, whose hosted image is a preview.
+ runs-on: macos-26
timeout-minutes: 60
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# The version identity is derived from git tags by MinVer; a shallow clone sees no tags
# and would stamp 0.0.0 into every manifest and binary.
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
@@ -377,7 +379,7 @@ jobs:
xcode_developer_dir="$(python3 - <<'PY'
import os
- print(os.path.realpath("/Applications/Xcode_26.0.app/Contents/Developer"))
+ print(os.path.realpath("/Applications/Xcode_26.6.app/Contents/Developer"))
PY
)"
if [ ! -d "${xcode_developer_dir}" ]; then
@@ -389,8 +391,8 @@ jobs:
xcodebuild -version
xcode_version="$(xcodebuild -version | awk 'NR == 1 { print $2 }')"
case "${xcode_version}" in
- 26.0*) ;;
- *) echo "Expected Xcode 26.0.x, got ${xcode_version}." >&2; exit 1 ;;
+ 26.6*) ;;
+ *) echo "Expected Xcode 26.6.x, got ${xcode_version}." >&2; exit 1 ;;
esac
xcrun --sdk macosx --show-sdk-path
xcrun --sdk iphonesimulator --show-sdk-path
@@ -411,7 +413,8 @@ jobs:
echo ".NET SDK ${dotnet_version}"
- name: Install iOS workload
- run: dotnet workload install ios --skip-manifest-update --disable-parallel --no-http-cache
+ # https://github.com/dotnet/macios/releases/tag/dotnet-10.0.1xx-xcode26.5-10318
+ run: dotnet workload install ios --version 10.0.401 --disable-parallel --no-http-cache
- name: Report iOS workload
run: |
diff --git a/.github/workflows/release-acp-sdk.yml b/.github/workflows/release-acp-sdk.yml
index c309222a..a49b0b9e 100644
--- a/.github/workflows/release-acp-sdk.yml
+++ b/.github/workflows/release-acp-sdk.yml
@@ -31,12 +31,12 @@ jobs:
# history (tags included) must be present. A shallow clone would pack a 0.0.0-alpha version
# and the tag/version gate below would catch it - but only after the whole pack had run.
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
@@ -60,7 +60,7 @@ jobs:
run: ./scripts/gates/run-acp-sdk-tag-version-gate.sh "${{ github.ref_name }}" "${{ env.PACKAGE_OUTPUT }}"
- name: Upload ACP SDK package
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: acp-sdk-release-package
path: |
@@ -74,15 +74,15 @@ jobs:
timeout-minutes: 25
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
- name: Download ACP SDK package
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: acp-sdk-release-package
path: artifacts/acp-sdk-package
@@ -122,19 +122,19 @@ jobs:
# Sparse: global.json is the only tracked file this job touches. The package it pushes comes
# from the `pack` job's artifact, not from source.
- name: Checkout for the SDK pin
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
sparse-checkout: global.json
sparse-checkout-cone-mode: false
fetch-depth: 1
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
- name: Download ACP SDK package
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: acp-sdk-release-package
path: artifacts/acp-sdk-package
diff --git a/.github/workflows/release-packaging.yml b/.github/workflows/release-packaging.yml
index a17eef61..746c5875 100644
--- a/.github/workflows/release-packaging.yml
+++ b/.github/workflows/release-packaging.yml
@@ -37,12 +37,12 @@ jobs:
timeout-minutes: 45
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
@@ -61,7 +61,7 @@ jobs:
run: scripts/gates/run-release-artifact-contract-gate.sh wasm publish/wasm
- name: Upload WebAssembly artifact
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: wasm-build
path: publish/wasm
@@ -72,12 +72,12 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
@@ -223,7 +223,7 @@ jobs:
(Join-Path $wixObjDir "Harvest.wixobj")
- name: Upload Desktop artifact
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: desktop-windows-build
path: publish/desktop-windows
@@ -260,23 +260,24 @@ jobs:
[void][Runtime.InteropServices.Marshal]::FinalReleaseComObject($installer)
}
- name: Upload Windows Skia MSI artifact
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: desktop-windows-skia-msi
path: artifacts/installer/*.msi
package-windows-msix:
name: Package Windows MSIX
- runs-on: windows-latest
+ # Match the .NET 10.0.4xx SDK with the Visual Studio 2026 XAML/MSIX toolchain.
+ runs-on: windows-2025-vs2026
timeout-minutes: 60
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
@@ -391,7 +392,7 @@ jobs:
./scripts/gates/run-msix-package-contract-gate.ps1 -Package $package.FullName
- name: Upload Windows MSIX artifact
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: desktop-windows-msix
path: artifacts/msix/**/*.msix
@@ -414,12 +415,12 @@ jobs:
MACOS_NOTARY_PROFILE: ${{ secrets.MACOS_NOTARY_PROFILE }}
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
@@ -549,7 +550,7 @@ jobs:
run: scripts/gates/run-macos-pkg-artifact-gate.sh "$PKG_PATH"
- name: Upload macOS installer package artifact
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: desktop-macos-pkg
path: |
@@ -558,14 +559,14 @@ jobs:
if-no-files-found: error
- name: Upload macOS bundle artifact
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: desktop-macos-bundle
path: publish/macos-bundle/*
- name: Upload macOS .dmg artifact
if: env.MACOS_APP_CODESIGN_KEY != '' && env.MACOS_DMG_CODESIGN_KEY != ''
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: desktop-macos-dmg
path: publish/dmg/*.dmg
@@ -576,14 +577,14 @@ jobs:
timeout-minutes: 60
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# The version identity is derived from git tags by MinVer; a shallow clone sees no tags and
# would stamp 0.0.0 into the package version, which no later release could upgrade over.
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
@@ -621,7 +622,7 @@ jobs:
run: scripts/gates/run-desktop-linux-package-smoke.sh "${{ steps.build-deb.outputs.deb-path }}"
- name: Upload Linux desktop package artifact
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: desktop-linux-deb
path: |
@@ -649,49 +650,49 @@ jobs:
steps:
- name: Download Windows MSIX artifact
if: needs.package-windows-msix.result == 'success'
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: desktop-windows-msix
path: windows-msix
- name: Download WebAssembly artifact
if: needs.package-wasm.result == 'success'
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: wasm-build
path: wasm-build
- name: Download Windows Skia MSI artifact
if: needs.package-desktop.result == 'success'
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: desktop-windows-skia-msi
path: windows-skia-msi
- name: Download macOS bundle artifact
if: needs.package-macos.result == 'success'
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: desktop-macos-bundle
path: macos-bundle
- name: Download macOS installer package artifact
if: needs.package-macos.result == 'success'
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: desktop-macos-pkg
path: macos-pkg
- name: Download macOS .dmg artifact
if: needs.package-macos.outputs.dmg-produced == 'true'
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: desktop-macos-dmg
path: macos-dmg
- name: Download Linux desktop package artifact
if: needs.package-linux-desktop.result == 'success'
- uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
+ uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: desktop-linux-deb
path: linux-deb
diff --git a/.github/workflows/terminal-auth-windows.yml b/.github/workflows/terminal-auth-windows.yml
index 6da47248..739efd6c 100644
--- a/.github/workflows/terminal-auth-windows.yml
+++ b/.github/workflows/terminal-auth-windows.yml
@@ -25,11 +25,11 @@ jobs:
MSBUILDDISABLENODEREUSE: 1
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
- name: Run actual ConPTY process and teardown gate
@@ -37,7 +37,7 @@ jobs:
run: ./scripts/gates/run-terminal-auth-windows-gate.ps1
- name: Upload terminal authentication results
if: always()
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: terminal-auth-windows-results
path: artifacts/terminal-auth-windows
diff --git a/.github/workflows/wasm-smoke-gates.yml b/.github/workflows/wasm-smoke-gates.yml
index 6b6a9080..c7e9a092 100644
--- a/.github/workflows/wasm-smoke-gates.yml
+++ b/.github/workflows/wasm-smoke-gates.yml
@@ -49,17 +49,17 @@ jobs:
timeout-minutes: 45
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
- name: Setup Node
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
- node-version: "24"
+ node-version: "26.10.0"
- name: Install browser dependencies
run: npx --yes playwright install --with-deps chromium
diff --git a/.github/workflows/windows-packaged-gui-acceptance.yml b/.github/workflows/windows-packaged-gui-acceptance.yml
index 4f342807..ca11e7dc 100644
--- a/.github/workflows/windows-packaged-gui-acceptance.yml
+++ b/.github/workflows/windows-packaged-gui-acceptance.yml
@@ -35,7 +35,8 @@ concurrency:
jobs:
packaged-gui:
name: Windows Installed WinUI GUI
- runs-on: windows-latest
+ # Match the .NET 10.0.4xx SDK with the Visual Studio 2026 XAML/MSIX toolchain.
+ runs-on: windows-2025-vs2026
timeout-minutes: 80
env:
DOTNET_PROCESSOR_COUNT: '2'
@@ -44,11 +45,11 @@ jobs:
UseSharedCompilation: 'false'
steps:
- name: Checkout
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
- name: Setup .NET
- uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5.4.0
+ uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
- name: Verify the hosted interactive desktop with real input
@@ -76,7 +77,7 @@ jobs:
run: python scripts/gates/windows-elicitation-reverse-verification.py
- name: Upload installed-product and desktop evidence
if: always()
- uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: windows-packaged-gui-acceptance
path: |
diff --git a/BUILD_GUIDE.md b/BUILD_GUIDE.md
index 021805b2..f1425c71 100644
--- a/BUILD_GUIDE.md
+++ b/BUILD_GUIDE.md
@@ -16,7 +16,7 @@ dotnet --version
dotnet --info
```
-仓库通过 `global.json` 锁定 .NET SDK `10.0.302`,允许同一 feature band 内的 patch 前滚;Uno SDK 锁定为 `6.7.30`。版本不匹配时先安装 .NET 10 SDK,不要手改项目 TFM 或 `global.json`。
+仓库通过 `global.json` 锁定 .NET SDK `10.0.401`,允许同一 feature band 内的 patch 前滚;Uno SDK 锁定为 `6.7.30`。所有子目录共用根目录的 `global.json`。版本不匹配时先安装 .NET 10 SDK,不要手改项目 TFM 或 `global.json`。
### 必需与可选依赖
@@ -24,7 +24,7 @@ dotnet --info
|------|--------------|
| Desktop / Core | .NET 10 SDK;Linux 还需要下方的原生库 |
| BrowserWasm | .NET 10 SDK + `wasm-tools` workload |
-| Windows MSIX | Visual Studio 18.8+ 或 Build Tools;MSBuild、C++ 构建工具、Windows SDK `10.0.26100.0`;`signtool` 使用 SDK `10.0.22621.0` |
+| Windows MSIX | Visual Studio 18.9.3+ 或 Build Tools;MSBuild、C++ 构建工具、Windows SDK `10.0.26100.0`;`signtool` 使用 SDK `10.0.22621.0` |
| Android | Android workload、Android SDK 和 Java JDK 17+;默认构建不启用 Android |
| iOS | macOS、匹配的 Xcode 和 iOS workload;默认构建不启用 iOS |
@@ -138,7 +138,7 @@ run.bat desktop
### 1. 环境要求
- **.NET SDK**: 10.0 或更高版本
- - 推荐版本:10.0.302(允许 patch 前滚)
+ - 推荐版本:10.0.401(允许 patch 前滚)
- 下载地址: https://dotnet.microsoft.com/download/dotnet/10.0
- **操作系统**:
@@ -163,7 +163,7 @@ Linux 桌面运行时依赖按能力分层(包名以 Debian/Ubuntu 为例)
# 检查 .NET SDK 版本
dotnet --version
-# 应该输出 10.0.302 或兼容的 10.0.3xx patch 版本
+# 应该输出 10.0.401 或兼容的 10.0.4xx patch 版本
```
### 3. 克隆代码(如果还没有)
@@ -227,7 +227,7 @@ run.bat
> 证书复用:`.tools/run-winui3-msix.ps1` 现在会复用同一张开发证书,不应再在每次 `run.bat msix` 时重建证书或反复要求安装证书。
> 历史根因:脚本曾使用 PowerShell 中不可靠的 `$Cert.GetRSAPrivateKey()` 调用来判断私钥可用性,导致有效的 RSA 私钥被误判为不可用,进而每次重建新证书;现已改为标准的 `RSACertificateExtensions.GetRSAPrivateKey(...)`。
> 工具链锁定:Windows SDK 10.0.26100.0,signtool 来自 SDK 10.0.22621.0。
-> Workload manifest:CI 应与 `global.json` 中的 .NET SDK feature band 保持一致;当前仓库锁定 10.0.302,并使用 `rollForward: latestPatch`。所有 GHA 均使用 `setup-dotnet` 官方支持的 `10.0.3xx` 语法,只在同一 feature band 内前滚 patch,避免 workload manifest 与 Xcode 独立漂移。
+> Workload manifest:CI 应与 `global.json` 中的 .NET SDK feature band 保持一致;当前仓库锁定 10.0.401,并使用 `rollForward: latestPatch`。所有 GHA 均使用 `setup-dotnet` 的 `global-json-file` 读取同一锁定文件。`global.json` 不设 `sdk.workloadVersion`:该字段会让 MSBuild 在未装 workload 的 runner 上直接解析失败,且与 `--skip-manifest-update` 互斥;workload 清单随 SDK feature band 走。iOS 编译与安装门禁在 `dotnet workload install ios` 上显式钉 workload set `10.0.401`(Apple workload 26.5.10318),共用 `macos-26` runner 上的 Xcode 26.6,对应 [Apple workload 发布说明](https://github.com/dotnet/macios/releases/tag/dotnet-10.0.1xx-xcode26.5-10318)。同 band 更新的 `10.0.401.1` 会切到要求 Xcode 27 的 27.0 workload,其托管镜像仍是 public preview,iOS 27 模拟器上安装与启动均不稳定,因此暂不跟进。
> 验证口径:`dotnet build -f net10.0-windows10.0.26100.0` 不是本仓库的权威 WinUI 3 / MSIX 门禁;Windows 原生包请以 `build.bat msix` 或 `.tools/run-winui3-msix.ps1 -SkipInstall` 为准。`dotnet build` 主要用于 Core/Skia/Desktop/Wasm 验证。
#### Linux Headless Desktop
diff --git a/README.en.md b/README.en.md
index 2be90e8e..a97724b0 100644
--- a/README.en.md
+++ b/README.en.md
@@ -51,7 +51,7 @@ Screenshots come from the store listing. In the app, **Settings → About** prov
## Tech Stack
- Uno Platform 6.7+ (repo pin: `Uno.Sdk` 6.7.30)
-- .NET 10 (repo pin: SDK 10.0.302, with 10.0.3xx patch roll-forward)
+- .NET 10 (repo pin: SDK 10.0.401, with 10.0.4xx patch roll-forward)
- WinUI 3 on Windows
- Clean Architecture + MVVM
@@ -78,8 +78,8 @@ For environment requirements and detailed build steps, start with [BUILD_GUIDE.m
### Requirements
-- .NET SDK **10.0.302** or a compatible **10.0.3xx** patch (see `global.json`)
-- Windows 10 1809+ / Windows 11 for WinUI 3 and MSIX validation; on Windows prefer Visual Studio **18.8+**
+- .NET SDK **10.0.401** or a compatible **10.0.4xx** patch (see `global.json`)
+- Windows 10 1809+ / Windows 11 for WinUI 3 and MSIX validation; on Windows prefer Visual Studio **18.9.3+**
- Or an equivalent command-line toolchain (Linux/macOS can build Desktop / WASM)
### Common Commands
diff --git a/README.md b/README.md
index f1091117..cdb818ad 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,7 @@ Salmon Egg 已在 Microsoft Store 上架,免费下载。商店页面的正式
## 技术栈
- Uno Platform 6.7+(仓库锁定 `Uno.Sdk` 6.7.30)
-- .NET 10(仓库锁定 SDK 10.0.302,允许 10.0.3xx patch 前滚)
+- .NET 10(仓库锁定 SDK 10.0.401,允许 10.0.4xx patch 前滚)
- WinUI 3(Windows)
- Clean Architecture + MVVM
@@ -80,8 +80,8 @@ iOS 原生 UIKit 在当前 Uno 6.7.103 下使用平台内的官方导航模板
### 环境要求
-- .NET SDK **10.0.302** 或兼容的 **10.0.3xx** patch(见 `global.json`)
-- Windows 10 1809+ / Windows 11(WinUI 3 / MSIX);Windows 建议 Visual Studio **18.8+**
+- .NET SDK **10.0.401** 或兼容的 **10.0.4xx** patch(见 `global.json`)
+- Windows 10 1809+ / Windows 11(WinUI 3 / MSIX);Windows 建议 Visual Studio **18.9.3+**
- 或等效命令行工具链(Linux/macOS 可构建 Desktop / WASM)
### 常用命令
diff --git a/SalmonEgg/Directory.Packages.props b/SalmonEgg/Directory.Packages.props
index e3a7018c..d4ce11d5 100644
--- a/SalmonEgg/Directory.Packages.props
+++ b/SalmonEgg/Directory.Packages.props
@@ -18,11 +18,12 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/SalmonEgg/global.json b/SalmonEgg/global.json
deleted file mode 100644
index 2e1711d9..00000000
--- a/SalmonEgg/global.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- // To update the version of Uno please update the version of the Uno.Sdk here. See https://aka.platform.uno/upgrade-uno-packages for more information.
- "msbuild-sdks": {
- "Uno.Sdk": "6.7.30"
- },
- "sdk": {
- "allowPrerelease": false
- }
-}
diff --git a/docs/release-guide.md b/docs/release-guide.md
index e7cecf47..e0b65233 100644
--- a/docs/release-guide.md
+++ b/docs/release-guide.md
@@ -21,7 +21,7 @@
### 通用要求
- .NET 10.0 SDK 或更高版本
-- Visual Studio 18.8+ 或 Visual Studio Code
+- Visual Studio 18.9.3+ 或 Visual Studio Code
- Git
### 平台特定要求
diff --git a/global.json b/global.json
index 44af54c7..971e0cd6 100644
--- a/global.json
+++ b/global.json
@@ -3,7 +3,7 @@
"Uno.Sdk": "6.7.30"
},
"sdk": {
- "version": "10.0.302",
+ "version": "10.0.401",
"rollForward": "latestPatch",
"allowPrerelease": false
},
diff --git a/scripts/gates/ios-elicitation-reverse-verification.py b/scripts/gates/ios-elicitation-reverse-verification.py
index 8ebff0ad..35dd64ab 100644
--- a/scripts/gates/ios-elicitation-reverse-verification.py
+++ b/scripts/gates/ios-elicitation-reverse-verification.py
@@ -18,9 +18,10 @@ def run(command, root, log_path, timeout):
return process.wait(timeout=timeout)
finally:
# The child gate handles SIGTERM by deleting its own Simulator and reaping its peer.
+ # macOS answers EPERM, not ESRCH, when the group is left with only zombies.
try:
os.killpg(process.pid, signal.SIGTERM)
- except ProcessLookupError:
+ except (ProcessLookupError, PermissionError):
pass
try:
process.wait(timeout=120)
@@ -28,7 +29,7 @@ def run(command, root, log_path, timeout):
pass
try:
os.killpg(process.pid, signal.SIGKILL)
- except ProcessLookupError:
+ except (ProcessLookupError, PermissionError):
pass
process.wait(timeout=5)
@@ -53,7 +54,7 @@ def main(args):
try:
source.write_bytes(mutant)
assert run(build, root, output / "mutant-build.log", 600) == 0, "The mutated app did not build"
- red_exit = run([*gate, str(output / "red")], root, output / "red.log", 900)
+ red_exit = run([*gate, str(output / "red")], root, output / "red.log", 1200)
assert red_exit != 0 and "The product opened a URL without consent." in (output / "red.log").read_text(), \
"The installed mutant was not rejected for its actual unconsented Safari visit"
red = json.loads((output / "red/peer-state.json").read_text())
@@ -62,7 +63,7 @@ def main(args):
source.write_bytes(original)
assert source.read_bytes() == original
assert run(build, root, output / "restored-build.log", 600) == 0, "The restored app did not rebuild"
- assert run([*gate, str(output / "restored")], root, output / "restored.log", 900) == 0
+ assert run([*gate, str(output / "restored")], root, output / "restored.log", 1200) == 0
baseline = json.loads((output.parent / "provenance.json").read_text())
red = json.loads((output / "red/provenance.json").read_text())
restored = json.loads((output / "restored/provenance.json").read_text())
diff --git a/scripts/gates/run-acp-published-bridge-gate.py b/scripts/gates/run-acp-published-bridge-gate.py
index d7599b9d..99c7d474 100644
--- a/scripts/gates/run-acp-published-bridge-gate.py
+++ b/scripts/gates/run-acp-published-bridge-gate.py
@@ -33,7 +33,7 @@ def main():
output.mkdir(parents=True, exist_ok=True)
bridge_path = shutil.which(args.bridge)
if bridge_path is None:
- raise SystemExit("Install acpremote==1.9.0 in an isolated environment before running this gate.")
+ raise SystemExit("Install the acpremote version pinned in .github/workflows/ci-core.yml in an isolated environment before running this gate.")
with socket.socket() as listener:
listener.bind(("127.0.0.1", 0))
port = listener.getsockname()[1]
diff --git a/scripts/gates/run-ios-elicitation-gate.py b/scripts/gates/run-ios-elicitation-gate.py
index 7a3102cb..25630bbb 100644
--- a/scripts/gates/run-ios-elicitation-gate.py
+++ b/scripts/gates/run-ios-elicitation-gate.py
@@ -25,6 +25,37 @@ def output(command):
return subprocess.check_output(command, text=True, timeout=90).strip()
+def capture_hang_evidence(artifacts, simulator, executable):
+ """Record where a stalled product is stuck before cleanup terminates it.
+
+ The app log stops at the first line it cannot get past, which names neither the thread nor the
+ frame; a host stack sample, the device's unified log and any crash report do.
+ """
+ diagnostics = artifacts / "hang-diagnostics"
+ diagnostics.mkdir(exist_ok=True)
+ # Simulator apps run as host processes launched from the device's own bundle container.
+ pattern = f"{simulator}/data/Containers/Bundle/Application/[^ ]*/{executable}.app/{executable}"
+ commands = []
+ try:
+ pids = subprocess.run(["pgrep", "-f", pattern], capture_output=True, text=True, timeout=10).stdout.split()
+ except subprocess.TimeoutExpired:
+ pids = []
+ commands += [(f"sample-{pid}.txt", ["sample", pid, "5"], 60) for pid in pids]
+ commands.append(("device-log.txt", ["xcrun", "simctl", "spawn", simulator, "log", "show", "--last", "20m",
+ "--style", "compact", "--predicate", f'process == "{executable}"'], 90))
+ for name, command, timeout in commands:
+ with (diagnostics / name).open("w") as log:
+ try:
+ subprocess.run(command, stdout=log, stderr=subprocess.STDOUT, timeout=timeout, check=False)
+ except subprocess.TimeoutExpired:
+ log.write(f"\n[ios-gate] timed out after {timeout}s\n")
+ except OSError as error:
+ log.write(f"\n[ios-gate] could not run: {error!r}\n")
+ for report in (Path.home() / "Library/Logs/DiagnosticReports").glob(f"{executable}*"):
+ shutil.copyfile(report, diagnostics / report.name)
+ print(f"[ios-gate] hang diagnostics: {len(pids)} product process(es) sampled", flush=True)
+
+
def seed(container, endpoint):
root = container / "Library/Application Support/SalmonEgg"
(root / "config/servers").mkdir(parents=True)
@@ -64,6 +95,7 @@ def main(args):
test_process = None
root = None
installed = False
+ passed = False
try:
# Start the peer before first-boot CoreSimulator work competes for the host CPU.
with socket.socket() as reservation:
@@ -99,8 +131,16 @@ def main(args):
with (artifacts / "simulator-boot.log").open("w") as boot_log:
run(["xcrun", "simctl", "bootstatus", simulator, "-b"], timeout=300,
stdout=boot_log, stderr=subprocess.STDOUT)
- run(["open", "-a", "Simulator", "--args", "-CurrentDeviceUDID", simulator])
- run(["xcrun", "simctl", "install", simulator, str(app)])
+ # Open the selected Xcode's simulator host by path rather than by LaunchServices name. Xcode 27
+ # replaced Developer/Applications/Simulator.app with Contents/Applications/DeviceHub.app.
+ developer = Path(output(["xcode-select", "-p"]))
+ candidates = [developer / "Applications/Simulator.app", developer.parent / "Applications/DeviceHub.app"]
+ simulator_app = next((path for path in candidates if path.is_dir()), None)
+ assert simulator_app is not None, ("The selected Xcode has no simulator host app: "
+ + ", ".join(f"{path.parent}={sorted(p.name for p in path.parent.glob('*.app'))}" for path in candidates))
+ run(["open", "-a", str(simulator_app), "--args", "-CurrentDeviceUDID", simulator])
+ # The Debug interpreter bundle took ~130 s to install on macos-26 and over 5 min on the Xcode 27 host.
+ run(["xcrun", "simctl", "install", simulator, str(app)], timeout=600)
installed = True
container = Path(output(["xcrun", "simctl", "get_app_container", simulator, bundle_id, "data"]))
root = seed(container, endpoints["endpoint"])
@@ -141,7 +181,9 @@ def main(args):
test_process = subprocess.Popen(["xcodebuild", "test-without-building", *test_arguments,
"-resultBundlePath", str(artifacts / "results.xcresult")],
env=env, stdout=log, stderr=subprocess.STDOUT, start_new_session=True)
- test_exit = test_process.wait(timeout=420)
+ # Measured end to end: ~240 s on Xcode 26.5, ~460 s on Xcode 26.6 (runner launch,
+ # the test itself and the result bundle are each slower there).
+ test_exit = test_process.wait(timeout=600)
if test_exit:
print((artifacts / "xcuitest.log").read_text(errors="replace")[-18000:])
assert test_exit == 0, "The installed product XCUITest failed"
@@ -162,16 +204,27 @@ def main(args):
"systemSafari": True, "browserVisits": 2, "acceptWithoutContent": True,
"formAnswer": True, "formResponses": 3, "noExternalDataPersistence": True}, indent=2) + "\n")
print("iOS installed product: consent, form, Safari isolation and completion passed")
+ passed = True
finally:
+ if installed and not passed:
+ # Sample while the product is still alive: every later step terminates it.
+ try:
+ capture_hang_evidence(artifacts, simulator, binary.name)
+ except Exception as error: # Evidence is best-effort; never mask the gate's own failure.
+ print(f"[ios-gate] hang diagnostics failed: {error!r}", flush=True)
if test_process is not None:
+ # macOS answers EPERM, not ESRCH, when the group is left with only zombies.
try:
os.killpg(test_process.pid, signal.SIGTERM)
- except ProcessLookupError:
+ except (ProcessLookupError, PermissionError):
pass
try:
test_process.wait(timeout=10)
except subprocess.TimeoutExpired:
- os.killpg(test_process.pid, signal.SIGKILL)
+ try:
+ os.killpg(test_process.pid, signal.SIGKILL)
+ except (ProcessLookupError, PermissionError):
+ pass
test_process.wait(timeout=5)
results = artifacts / "results.xcresult"
if results.exists():
diff --git a/src/SalmonEgg.Infrastructure.Desktop/SalmonEgg.Infrastructure.Desktop.csproj b/src/SalmonEgg.Infrastructure.Desktop/SalmonEgg.Infrastructure.Desktop.csproj
index 97d4fccf..4df4c7a8 100644
--- a/src/SalmonEgg.Infrastructure.Desktop/SalmonEgg.Infrastructure.Desktop.csproj
+++ b/src/SalmonEgg.Infrastructure.Desktop/SalmonEgg.Infrastructure.Desktop.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/src/SalmonEgg.Infrastructure/SalmonEgg.Infrastructure.csproj b/src/SalmonEgg.Infrastructure/SalmonEgg.Infrastructure.csproj
index 065acb49..9e33300f 100644
--- a/src/SalmonEgg.Infrastructure/SalmonEgg.Infrastructure.csproj
+++ b/src/SalmonEgg.Infrastructure/SalmonEgg.Infrastructure.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/tests/SalmonEgg.Acp.Tests/SalmonEgg.Acp.Tests.csproj b/tests/SalmonEgg.Acp.Tests/SalmonEgg.Acp.Tests.csproj
index 5b410f3e..bdbdd38b 100644
--- a/tests/SalmonEgg.Acp.Tests/SalmonEgg.Acp.Tests.csproj
+++ b/tests/SalmonEgg.Acp.Tests/SalmonEgg.Acp.Tests.csproj
@@ -15,9 +15,9 @@
-
+
-
+
diff --git a/tests/SalmonEgg.Application.Tests/SalmonEgg.Application.Tests.csproj b/tests/SalmonEgg.Application.Tests/SalmonEgg.Application.Tests.csproj
index 40a10e18..c2431dcc 100644
--- a/tests/SalmonEgg.Application.Tests/SalmonEgg.Application.Tests.csproj
+++ b/tests/SalmonEgg.Application.Tests/SalmonEgg.Application.Tests.csproj
@@ -11,9 +11,9 @@
-
+
-
+
diff --git a/tests/SalmonEgg.Cli.Tests/SalmonEgg.Cli.Tests.csproj b/tests/SalmonEgg.Cli.Tests/SalmonEgg.Cli.Tests.csproj
index d809fca8..844b9ad9 100644
--- a/tests/SalmonEgg.Cli.Tests/SalmonEgg.Cli.Tests.csproj
+++ b/tests/SalmonEgg.Cli.Tests/SalmonEgg.Cli.Tests.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/tests/SalmonEgg.Domain.Tests/SalmonEgg.Domain.Tests.csproj b/tests/SalmonEgg.Domain.Tests/SalmonEgg.Domain.Tests.csproj
index 4d34ce76..3e2bb8da 100644
--- a/tests/SalmonEgg.Domain.Tests/SalmonEgg.Domain.Tests.csproj
+++ b/tests/SalmonEgg.Domain.Tests/SalmonEgg.Domain.Tests.csproj
@@ -9,7 +9,7 @@
-
+
diff --git a/tests/SalmonEgg.GuiTests.Windows/SalmonEgg.GuiTests.Windows.csproj b/tests/SalmonEgg.GuiTests.Windows/SalmonEgg.GuiTests.Windows.csproj
index 9db1d889..23ad6c95 100644
--- a/tests/SalmonEgg.GuiTests.Windows/SalmonEgg.GuiTests.Windows.csproj
+++ b/tests/SalmonEgg.GuiTests.Windows/SalmonEgg.GuiTests.Windows.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/tests/SalmonEgg.Infrastructure.Tests/SalmonEgg.Infrastructure.Tests.csproj b/tests/SalmonEgg.Infrastructure.Tests/SalmonEgg.Infrastructure.Tests.csproj
index 18bf656e..fb60519c 100644
--- a/tests/SalmonEgg.Infrastructure.Tests/SalmonEgg.Infrastructure.Tests.csproj
+++ b/tests/SalmonEgg.Infrastructure.Tests/SalmonEgg.Infrastructure.Tests.csproj
@@ -11,10 +11,10 @@
-
+
-
+
diff --git a/tests/SalmonEgg.Presentation.Core.Tests/SalmonEgg.Presentation.Core.Tests.csproj b/tests/SalmonEgg.Presentation.Core.Tests/SalmonEgg.Presentation.Core.Tests.csproj
index 2d6f173b..6729728c 100644
--- a/tests/SalmonEgg.Presentation.Core.Tests/SalmonEgg.Presentation.Core.Tests.csproj
+++ b/tests/SalmonEgg.Presentation.Core.Tests/SalmonEgg.Presentation.Core.Tests.csproj
@@ -9,8 +9,8 @@
-
-
+
+
diff --git a/tests/SalmonEgg.Presentation.Core.Tests/WasmStartupAssetsTests.cs b/tests/SalmonEgg.Presentation.Core.Tests/WasmStartupAssetsTests.cs
index 9ecf3e18..aeb5752e 100644
--- a/tests/SalmonEgg.Presentation.Core.Tests/WasmStartupAssetsTests.cs
+++ b/tests/SalmonEgg.Presentation.Core.Tests/WasmStartupAssetsTests.cs
@@ -317,12 +317,14 @@ public void PlatformBuildGate_IsolatesMobileRestoreGraphsAndPinsIosToolchain()
Assert.True(
gate.IndexOf("Verify iOS .NET SDK", StringComparison.Ordinal)
< gate.IndexOf("Install iOS workload", StringComparison.Ordinal));
- Assert.Contains("runs-on: macos-15", gate, StringComparison.Ordinal);
- Assert.Contains("os.path.realpath(\"/Applications/Xcode_26.0.app/Contents/Developer\")", gate, StringComparison.Ordinal);
+ Assert.Contains("runs-on: macos-26", gate, StringComparison.Ordinal);
+ // The band's newest workload set requires Xcode 27; the pinned set keeps the job on the GA image.
+ Assert.Contains("dotnet workload install ios --version 10.0.401 ", gate, StringComparison.Ordinal);
+ Assert.Contains("os.path.realpath(\"/Applications/Xcode_26.6.app/Contents/Developer\")", gate, StringComparison.Ordinal);
Assert.Contains("sudo xcode-select --switch \"${xcode_developer_dir}\"", gate, StringComparison.Ordinal);
- Assert.DoesNotContain("xcode-select --switch /Applications/Xcode_26.0.app/Contents/Developer", gate, StringComparison.Ordinal);
+ Assert.DoesNotContain("xcode-select --switch /Applications/Xcode_", gate, StringComparison.Ordinal);
Assert.Contains("xcodebuild -version", gate, StringComparison.Ordinal);
- Assert.Contains("26.0*)", gate, StringComparison.Ordinal);
+ Assert.Contains("26.6*)", gate, StringComparison.Ordinal);
Assert.Contains("xcrun --sdk macosx --show-sdk-path", gate, StringComparison.Ordinal);
Assert.Contains("xcrun --sdk iphonesimulator --show-sdk-path", gate, StringComparison.Ordinal);
Assert.Contains("xcrun --sdk macosx --find actool", gate, StringComparison.Ordinal);
@@ -357,7 +359,6 @@ public void DotnetWorkflows_PinRepositorySdkFeatureBand()
@".github\workflows\wasm-smoke-gates.yml"
];
- Assert.Contains("\"version\": \"10.0.302\"", globalJson, StringComparison.Ordinal);
Assert.Contains("\"rollForward\": \"latestPatch\"", globalJson, StringComparison.Ordinal);
foreach (var workflowPath in workflowPaths)
{