setup-local-sdk

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

setup-local-sdk

本地SDK安装指南

Purpose

目的

Guide the user through installing a .NET SDK into a project-local
.dotnet/
directory and wiring it up via the
global.json
paths
feature (.NET 10+). The examples use .NET 11, but this works with any version — prerelease or stable.
The result is a fully isolated SDK that:
  • Does not modify the system-wide .NET installation.
  • Is picked up automatically by
    dotnet
    commands from the project root.
  • Can be deleted to revert (
    rm -rf .dotnet/
    or
    Remove-Item -Recurse -Force .\.dotnet
    ).
指导用户将.NET SDK安装到项目本地的
.dotnet/
目录,并通过
global.json
paths
功能(.NET 10及以上版本)进行配置。示例中使用的是.NET 11,但该方法适用于所有版本——预发布版或稳定版均可。
最终得到的是一个完全隔离的SDK,具备以下特性:
  • 不会修改系统级的.NET安装。
  • 位于项目根目录时,会被
    dotnet
    命令自动识别。
  • 可通过删除目录恢复(执行
    rm -rf .dotnet/
    Remove-Item -Recurse -Force .\\.dotnet
    )。

When NOT to use

不适用场景

  • User wants a system-wide install — direct to the official installer.
  • Host
    dotnet
    is older than v10
    paths
    doesn't exist; explain and stop.
  • User needs a runtime-only install —
    paths
    applies to SDK resolution only.
  • 用户需要系统级安装——直接使用官方安装程序。
  • 宿主
    dotnet
    版本低于v10——
    paths
    功能不存在;需告知用户并终止操作。
  • 用户仅需仅运行时安装——
    paths
    仅适用于SDK解析。

Inputs / Prerequisites

输入项 / 前置条件

InputRequiredDefaultNotes
Channel or versionNo
11.0
e.g.
11.0
,
STS
,
LTS
, or an exact version like
11.0.100-preview.2.26159.112
QualityNo
preview
One of:
daily
,
preview
,
ga
jqNoOptional for bash team scripts when patching an existing
global.json
; without it, do not overwrite the file
输入项是否必填默认值说明
通道或版本
11.0
例如
11.0
STS
LTS
,或精确版本如
11.0.100-preview.2.26159.112
版本质量
preview
可选值:
daily
(每日构建版)、
preview
(预览版)、
ga
(正式版)
jq在bash团队脚本中修补现有
global.json
时可选;若无jq,请勿覆盖文件

Prerequisites

前置条件

  1. A .NET 10+ SDK is installed globally — run
    dotnet --version
    ; major ≥ 10.
  2. curl (macOS/Linux) or PowerShell (Windows) is available.
  1. 已全局安装.NET 10及以上版本的SDK——运行
    dotnet --version
    检查;主版本号≥10。
  2. 已安装curl(macOS/Linux)或PowerShell(Windows)。

Workflow

操作流程

Step 1 — Clarify what to install

步骤1 — 明确安装需求

If the user didn't specify, ask what .NET SDK version they want (e.g., "latest .NET 11 preview" or an exact version like
11.0.100-preview.2.26159.112
). Map the answer to
--channel
/
--quality
or
--version
flags.
若用户未指定,询问其想要安装的.NET SDK版本(例如“最新.NET 11预览版”或精确版本
11.0.100-preview.2.26159.112
)。将用户的回答映射为
--channel
/
--quality
--version
参数。

Step 2 — Verify .NET 10+ host

步骤2 — 验证.NET 10+宿主版本

If the user already provided
dotnet --version
output, treat that as the authoritative version for their machine. Do not override it with the agent workspace's version; if the two differ, explain that the workspace differs and continue advising for the user's machine.
bash
dotnet --version
If major version < 10, stop before downloading anything: the
paths
feature requires a .NET 10+ host SDK. Tell the user to install .NET 10 or later system-wide first, then return to the local SDK setup.
若用户已提供
dotnet --version
的输出结果,则以此作为其机器上的权威版本。请勿使用Agent工作区的版本覆盖该值;若两者不同,需说明工作区版本与用户机器版本存在差异,并继续针对用户机器提供指导。
bash
dotnet --version
若主版本号<10,在下载任何内容前终止操作:
paths
功能需要.NET 10及以上版本的宿主SDK。告知用户先全局安装.NET 10或更高版本,再返回本地SDK安装流程。

Step 3 — Detect operating system

步骤3 — 检测操作系统

Run
uname -s 2>/dev/null
. If it succeeds (including
MINGW*
,
MSYS*
,
CYGWIN*
— these are bash-capable environments like Git Bash) → use bash/
dotnet-install.sh
. If it fails (native Windows without Git Bash) → use PowerShell/
dotnet-install.ps1
.
运行
uname -s 2>/dev/null
。若命令执行成功(包括
MINGW*
MSYS*
CYGWIN*
——这些是支持bash的环境,如Git Bash)→ 使用bash/
dotnet-install.sh
脚本。若命令执行失败(原生Windows且未安装Git Bash)→ 使用PowerShell/
dotnet-install.ps1
脚本。

Step 4 — Check for existing local SDK

步骤4 — 检查现有本地SDK

macOS / Linux:
bash
test -d .dotnet && echo "exists" || echo "not found"
Windows (PowerShell):
powershell
if (Test-Path -LiteralPath .\.dotnet) { "exists" } else { "not found" }
If
.dotnet/
exists, ask: update with the new version, or skip and keep it?
macOS / Linux:
bash
test -d .dotnet && echo "exists" || echo "not found"
Windows(PowerShell):
powershell
if (Test-Path -LiteralPath .\\.dotnet) { "exists" } else { "not found" }
.dotnet/
目录已存在,询问用户:是使用新版本更新,还是保留现有版本跳过安装?

Step 5 — Download and run the install script

步骤5 — 下载并运行安装脚本

macOS / Linux:
bash
INSTALL_SCRIPT="$(mktemp "${TMPDIR:-/tmp}/dotnet-install.XXXXXX")"
trap 'rm -f "$INSTALL_SCRIPT"' EXIT
curl -fsSL https://dot.net/v1/dotnet-install.sh -o "$INSTALL_SCRIPT"
bash "$INSTALL_SCRIPT" --channel <CHANNEL> --quality <QUALITY> --install-dir .dotnet
Windows (PowerShell):
powershell
$installScript = Join-Path $env:TEMP "dotnet-install-$([guid]::NewGuid()).ps1"
try {
    Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile $installScript
    & $installScript -Channel <CHANNEL> -Quality <QUALITY> -InstallDir .dotnet
}
finally {
    if (Test-Path -LiteralPath $installScript) {
        Remove-Item -LiteralPath $installScript -Force
    }
}
For exact versions: use
--version <VERSION>
(bash) or
-Version <VERSION>
(PowerShell) instead of channel/quality flags. The install scripts are from Microsoft's official URLs:
https://dot.net/v1/dotnet-install.sh
and
https://dot.net/v1/dotnet-install.ps1
.
macOS / Linux:
bash
INSTALL_SCRIPT="$(mktemp "${TMPDIR:-/tmp}/dotnet-install.XXXXXX")"
trap 'rm -f "$INSTALL_SCRIPT"' EXIT
curl -fsSL https://dot.net/v1/dotnet-install.sh -o "$INSTALL_SCRIPT"
bash "$INSTALL_SCRIPT" --channel <CHANNEL> --quality <QUALITY> --install-dir .dotnet
Windows(PowerShell):
powershell
$installScript = Join-Path $env:TEMP "dotnet-install-$([guid]::NewGuid()).ps1"
try {
    Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile $installScript
    & $installScript -Channel <CHANNEL> -Quality <QUALITY> -InstallDir .dotnet
}
finally {
    if (Test-Path -LiteralPath $installScript) {
        Remove-Item -LiteralPath $installScript -Force
    }
}
若安装精确版本:使用
--version <VERSION>
(bash)或
-Version <VERSION>
(PowerShell)参数替代通道/版本质量参数。安装脚本来自微软官方地址:
https://dot.net/v1/dotnet-install.sh
https://dot.net/v1/dotnet-install.ps1

Step 6 — Identify the installed version

步骤6 — 确认已安装版本

bash
./.dotnet/dotnet --version          # macOS/Linux
.\.dotnet\dotnet.exe --version      # Windows
Record the exact version string (e.g.,
11.0.100-preview.2.26159.112
) for
global.json
.
bash
./.dotnet/dotnet --version          # macOS/Linux
.\\.dotnet\\dotnet.exe --version      # Windows
记录精确版本字符串(例如
11.0.100-preview.2.26159.112
),用于配置
global.json

Step 7 — Create or update global.json

步骤7 — 创建或更新global.json

json
{
  "sdk": {
    "version": "<INSTALLED_VERSION>",
    "allowPrerelease": true,
    "rollForward": "latestFeature",
    "paths": [".dotnet", "$host$"],
    "errorMessage": "Required .NET SDK not found. Run ./install-dotnet.sh (or .ps1) to install it locally."
  }
}
  • paths
    :
    .dotnet
    first (local priority),
    $host$
    = system-wide fallback.
  • rollForward: "latestFeature"
    : use for latest-preview or floating feature-band installs.
  • Exact version requests: use
    rollForward: "disable"
    so SDK resolution doesn't move to a different feature band.
  • allowPrerelease
    : set to
    true
    only when installing a prerelease SDK. Omit for stable versions.
  • errorMessage
    : include only when team install scripts are created (Step 10). Otherwise omit.
If
global.json
already exists, merge carefully: preserve existing properties (
msbuild-sdks
,
tools
, etc.) and only add/update the
sdk
section. Read the existing file first, update/add the
sdk
object, then write it back. This ensures cross-project config (e.g., MSBuild settings) isn't lost. Always back up the original file (e.g.,
global.json.bak
) before modifying.
Minimal config (when version pinning isn't needed):
{"sdk":{"paths":[".dotnet","$host$"]}}
json
{
  "sdk": {
    "version": "<INSTALLED_VERSION>",
    "allowPrerelease": true,
    "rollForward": "latestFeature",
    "paths": [".dotnet", "$host$"],
    "errorMessage": "Required .NET SDK not found. Run ./install-dotnet.sh (or .ps1) to install it locally."
  }
}
  • paths
    :优先使用
    .dotnet
    (本地目录),
    $host$
    表示系统级安装作为 fallback。
  • rollForward: "latestFeature"
    :用于安装最新预览版或浮动功能带版本时。
  • 若要求精确版本:使用
    rollForward: "disable"
    ,确保SDK解析不会切换到其他功能带。
  • allowPrerelease
    :仅在安装预发布版SDK时设置为
    true
    。稳定版可省略该配置。
  • errorMessage
    :仅在创建团队安装脚本(步骤10)时添加。否则请省略。
global.json
已存在,需谨慎合并:保留现有属性(如
msbuild-sdks
tools
等),仅添加或更新
sdk
部分。需先读取现有文件,更新或添加
sdk
对象,再写回文件。这样可确保跨项目配置(如MSBuild设置)不会丢失。修改前请务必备份原始文件(例如
global.json.bak
)。
极简配置(无需固定版本时):
{"sdk":{"paths":[".dotnet","$host$"]}}

Step 8 — Update .gitignore

步骤8 — 更新.gitignore

macOS / Linux (or Git Bash):
bash
grep -qxF '.dotnet/' .gitignore 2>/dev/null || printf '\n.dotnet/\n' >> .gitignore
Windows (PowerShell):
powershell
if (-not (Test-Path .gitignore) -or -not (Select-String -Path .gitignore -Pattern '^\.dotnet/$' -Quiet)) {
    Add-Content -Path .gitignore -Value '.dotnet/'
}
macOS / Linux(或Git Bash):
bash
grep -qxF '.dotnet/' .gitignore 2>/dev/null || printf '\
.dotnet/\
' >> .gitignore
Windows(PowerShell):
powershell
if (-not (Test-Path .gitignore) -or -not (Select-String -Path .gitignore -Pattern '^\\.dotnet/$' -Quiet)) {
    Add-Content -Path .gitignore -Value '.dotnet/'
}

Step 9 — Install workloads (if requested)

步骤9 — 安装工作负载(如有需求)

Only do this after
global.json
and
.gitignore
are complete, so a slow or platform-limited workload install does not prevent the base local SDK setup from being usable.
If the user mentioned MAUI, mobile, workload, Blazor WASM, or cross-platform, install using the local binary (no sudo needed):
bash
./.dotnet/dotnet workload install <workload>       # macOS/Linux
.\.dotnet\dotnet.exe workload install <workload>   # Windows
Verify:
./.dotnet/dotnet workload list
(or
.\.dotnet\dotnet.exe workload list
).
For MAUI, pick a workload supported by the current OS and target platform. On Linux, the full
maui
meta-workload is not available; use a supported workload such as
maui-android
when Android is the target, or explain the platform limitation and ask which target to configure.
Always use the local dotnet binary for workload commands. Workload metadata is stored relative to the host process's dotnet root. The system
dotnet
puts metadata in the wrong location. (See dotnet/sdk#49825.)
仅在
global.json
.gitignore
配置完成后执行此步骤,避免缓慢或受平台限制的工作负载安装影响基础本地SDK的可用性。
若用户提到MAUI、移动开发、工作负载、Blazor WASM或跨平台开发,使用本地二进制文件进行安装(无需sudo权限):
bash
./.dotnet/dotnet workload install <workload>       # macOS/Linux
.\\.dotnet\\dotnet.exe workload install <workload>   # Windows
验证安装:执行
./.dotnet/dotnet workload list
(或
.\.dotnet\dotnet.exe workload list
)。
对于MAUI,请选择当前操作系统和目标平台支持的工作负载。在Linux上,完整的
maui
元工作负载不可用;请使用支持的工作负载,例如当目标平台为Android时使用
maui-android
,或说明平台限制并询问用户需要配置哪个目标平台。
始终使用本地dotnet二进制文件执行工作负载命令。 工作负载元数据存储在宿主进程的dotnet根目录相对路径下。系统级
dotnet
会将元数据存储在错误位置。(详情见dotnet/sdk#49825。)

Step 10 — Create team install scripts

步骤10 — 创建团队安装脚本

Create if user mentioned "team", "share", "CI", "scripts", etc. Otherwise offer. These examples back up
global.json
and preserve existing settings. The bash script uses
jq
when an existing
global.json
must be patched; if
jq
is unavailable, it refuses to overwrite the file and prints the settings to merge manually. Adapt script variables to the install choice from Step 1: exact versions should use
--version
/
-Version
and
rollForward: "disable"
; channel installs should use channel/quality and only set
allowPrerelease: true
for prerelease SDKs. If
global.json
already pins
sdk.version
and the user mainly needs team scripts, reuse that version in the scripts and update
global.json
first; do not start a long SDK download just to discover the version. When the user asks for both setup and scripts, create the scripts/config before any long install so the reproducible setup exists even if download or workload installation is slow.
install-dotnet.sh:
bash
#!/usr/bin/env bash
set -euo pipefail
INSTALL_DIR=".dotnet"
CHANNEL="11.0"
QUALITY="preview"
VERSION=""
ROLL_FORWARD="latestFeature"
ALLOW_PRERELEASE="true"
WORKLOADS=("${@}")
ERROR_MESSAGE="Required .NET SDK not found. Run ./install-dotnet.sh (or .ps1) to install it locally."
INSTALL_SCRIPT="$(mktemp "${TMPDIR:-/tmp}/dotnet-install.XXXXXX")"
GLOBAL_JSON_TMP=""
cleanup() {
    rm -f "$INSTALL_SCRIPT"
    [ -n "$GLOBAL_JSON_TMP" ] && rm -f "$GLOBAL_JSON_TMP"
}
trap cleanup EXIT
curl -fsSL https://dot.net/v1/dotnet-install.sh -o "$INSTALL_SCRIPT"
INSTALL_ARGS=(--install-dir "$INSTALL_DIR")
if [ -n "$VERSION" ]; then
    INSTALL_ARGS+=(--version "$VERSION")
    ROLL_FORWARD="disable"
else
    INSTALL_ARGS+=(--channel "$CHANNEL" --quality "$QUALITY")
fi
bash "$INSTALL_SCRIPT" "${INSTALL_ARGS[@]}"
SDK_VERSION=$("$INSTALL_DIR/dotnet" --version)
write_global_json() {
    if [ -f global.json ]; then
        cp global.json global.json.bak
        if ! command -v jq >/dev/null 2>&1; then
            echo "global.json exists; install succeeded, but this script will not overwrite it without jq." >&2
            echo "Merge these sdk settings manually so existing global.json properties are preserved:" >&2
            cat >&2 <<EOF
{
  "sdk": {
    "version": "$SDK_VERSION",
    "allowPrerelease": $ALLOW_PRERELEASE,
    "rollForward": "$ROLL_FORWARD",
    "paths": [".dotnet", "\$host\$"],
    "errorMessage": "$ERROR_MESSAGE"
  }
}
EOF
            exit 1
        fi
        GLOBAL_JSON_TMP="$(mktemp "${TMPDIR:-/tmp}/global-json.XXXXXX")"
        jq --arg version "$SDK_VERSION" --arg rollForward "$ROLL_FORWARD" --argjson allowPrerelease "$ALLOW_PRERELEASE" --arg errorMessage "$ERROR_MESSAGE" '
          .sdk = ((.sdk // {}) + {
            version: $version,
            allowPrerelease: $allowPrerelease,
            rollForward: $rollForward,
            paths: [".dotnet", "$host$"],
            errorMessage: $errorMessage
          })
        ' global.json > "$GLOBAL_JSON_TMP"
        mv "$GLOBAL_JSON_TMP" global.json
        GLOBAL_JSON_TMP=""
    else
        cat > global.json <<EOF
{
  "sdk": {
    "version": "$SDK_VERSION",
    "allowPrerelease": $ALLOW_PRERELEASE,
    "rollForward": "$ROLL_FORWARD",
    "paths": [".dotnet", "\$host\$"],
    "errorMessage": "$ERROR_MESSAGE"
  }
}
EOF
    fi
}
write_global_json
grep -qxF '.dotnet/' .gitignore 2>/dev/null || printf '\n.dotnet/\n' >> .gitignore
[ ${#WORKLOADS[@]} -gt 0 ] && "$INSTALL_DIR/dotnet" workload install "${WORKLOADS[@]}"
echo "Done. SDK: $SDK_VERSION"
bash
chmod +x install-dotnet.sh
install-dotnet.ps1:
powershell
param([string[]]$Workloads = @())
$ErrorActionPreference = 'Stop'
$installDir = '.dotnet'; $channel = '11.0'; $quality = 'preview'
$version = ''; $rollForward = 'latestFeature'; $allowPrerelease = $true
$errorMessage = 'Required .NET SDK not found. Run ./install-dotnet.sh (or .ps1) to install it locally.'
$installScript = Join-Path $env:TEMP "dotnet-install-$([guid]::NewGuid()).ps1"
try {
    Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile $installScript
    $installArgs = @('-InstallDir', $installDir)
    if ($version) {
        $installArgs += @('-Version', $version)
        $rollForward = 'disable'
    } else {
        $installArgs += @('-Channel', $channel, '-Quality', $quality)
    }
    & $installScript @installArgs
}
finally {
    if (Test-Path -LiteralPath $installScript) {
        Remove-Item -LiteralPath $installScript -Force
    }
}
$sdkVersion = & "$installDir\dotnet.exe" --version
$globalJson = if (Test-Path 'global.json') {
    Copy-Item 'global.json' 'global.json.bak'
    Get-Content -Path 'global.json' -Raw | ConvertFrom-Json
} else {
    [pscustomobject]@{}
}
if (-not $globalJson.PSObject.Properties['sdk']) {
    $globalJson | Add-Member -MemberType NoteProperty -Name 'sdk' -Value ([pscustomobject]@{})
}
$updates = [ordered]@{
    version = $sdkVersion
    allowPrerelease = $allowPrerelease
    rollForward = $rollForward
    paths = @('.dotnet', '$host$')
    errorMessage = $errorMessage
}
foreach ($entry in $updates.GetEnumerator()) {
    $property = $globalJson.sdk.PSObject.Properties[$entry.Key]
    if ($property) {
        $property.Value = $entry.Value
    } else {
        $globalJson.sdk | Add-Member -MemberType NoteProperty -Name $entry.Key -Value $entry.Value
    }
}
$globalJson | ConvertTo-Json -Depth 10 | Set-Content -Path 'global.json' -Encoding UTF8
if (-not (Test-Path .gitignore) -or -not (Select-String -Path .gitignore -Pattern '^\.dotnet/$' -Quiet)) {
    Add-Content -Path .gitignore -Value '.dotnet/'
}
if ($Workloads.Count -gt 0) { & "$installDir\dotnet.exe" workload install @Workloads }
Write-Host "Done. SDK: $sdkVersion"
Commit these scripts to the repo so teammates can run them.
若用户提到“团队”、“共享”、“CI”、“脚本”等关键词,则创建脚本。否则可主动提供。以下示例会备份
global.json
并保留现有设置。bash脚本在需要修补现有
global.json
时使用
jq
;若未安装jq,脚本会拒绝覆盖文件,并打印需手动合并的配置。根据步骤1的安装选择调整脚本变量:精确版本应使用
--version
/
-Version
参数和
rollForward: "disable"
;通道安装应使用通道/版本质量参数,且仅在安装预发布版SDK时设置
allowPrerelease: true
。若
global.json
已固定
sdk.version
且用户主要需要团队脚本,请在脚本中复用该版本并先更新
global.json
;不要为了获取版本而启动长时间的SDK下载。当用户同时要求安装和脚本时,请在进行长时间安装前创建脚本/配置,确保即使下载或工作负载安装缓慢,可复现的安装环境依然存在。
install-dotnet.sh:
bash
#!/usr/bin/env bash
set -euo pipefail
INSTALL_DIR=".dotnet"
CHANNEL="11.0"
QUALITY="preview"
VERSION=""
ROLL_FORWARD="latestFeature"
ALLOW_PRERELEASE="true"
WORKLOADS=("${@}")
ERROR_MESSAGE="Required .NET SDK not found. Run ./install-dotnet.sh (or .ps1) to install it locally."
INSTALL_SCRIPT="$(mktemp "${TMPDIR:-/tmp}/dotnet-install.XXXXXX")"
GLOBAL_JSON_TMP=""
cleanup() {
    rm -f "$INSTALL_SCRIPT"
    [ -n "$GLOBAL_JSON_TMP" ] && rm -f "$GLOBAL_JSON_TMP"
}
trap cleanup EXIT
curl -fsSL https://dot.net/v1/dotnet-install.sh -o "$INSTALL_SCRIPT"
INSTALL_ARGS=(--install-dir "$INSTALL_DIR")
if [ -n "$VERSION" ]; then
    INSTALL_ARGS+=(--version "$VERSION")
    ROLL_FORWARD="disable"
else
    INSTALL_ARGS+=(--channel "$CHANNEL" --quality "$QUALITY")
fi
bash "$INSTALL_SCRIPT" "${INSTALL_ARGS[@]}"
SDK_VERSION=$("$INSTALL_DIR/dotnet" --version)
write_global_json() {
    if [ -f global.json ]; then
        cp global.json global.json.bak
        if ! command -v jq >/dev/null 2>&1; then
            echo "global.json exists; install succeeded, but this script will not overwrite it without jq." >&2
            echo "Merge these sdk settings manually so existing global.json properties are preserved:" >&2
            cat >&2 <<EOF
{
  "sdk": {
    "version": "$SDK_VERSION",
    "allowPrerelease": $ALLOW_PRERELEASE,
    "rollForward": "$ROLL_FORWARD",
    "paths": [".dotnet", "\\$host\\$"],
    "errorMessage": "$ERROR_MESSAGE"
  }
}
EOF
            exit 1
        fi
        GLOBAL_JSON_TMP="$(mktemp "${TMPDIR:-/tmp}/global-json.XXXXXX")"
        jq --arg version "$SDK_VERSION" --arg rollForward "$ROLL_FORWARD" --argjson allowPrerelease "$ALLOW_PRERELEASE" --arg errorMessage "$ERROR_MESSAGE" '
          .sdk = ((.sdk // {}) + {
            version: $version,
            allowPrerelease: $allowPrerelease,
            rollForward: $rollForward,
            paths: [".dotnet", "$host$"],
            errorMessage: $errorMessage
          })
        ' global.json > "$GLOBAL_JSON_TMP"
        mv "$GLOBAL_JSON_TMP" global.json
        GLOBAL_JSON_TMP=""
    else
        cat > global.json <<EOF
{
  "sdk": {
    "version": "$SDK_VERSION",
    "allowPrerelease": $ALLOW_PRERELEASE,
    "rollForward": "$ROLL_FORWARD",
    "paths": [".dotnet", "\\$host\\$"],
    "errorMessage": "$ERROR_MESSAGE"
  }
}
EOF
    fi
}
write_global_json
grep -qxF '.dotnet/' .gitignore 2>/dev/null || printf '\
.dotnet/\
' >> .gitignore
[ ${#WORKLOADS[@]} -gt 0 ] && "$INSTALL_DIR/dotnet" workload install "${WORKLOADS[@]}"
echo "Done. SDK: $SDK_VERSION"
bash
chmod +x install-dotnet.sh
install-dotnet.ps1:
powershell
param([string[]]$Workloads = @())
$ErrorActionPreference = 'Stop'
$installDir = '.dotnet'; $channel = '11.0'; $quality = 'preview'
$version = ''; $rollForward = 'latestFeature'; $allowPrerelease = $true
$errorMessage = 'Required .NET SDK not found. Run ./install-dotnet.sh (or .ps1) to install it locally.'
$installScript = Join-Path $env:TEMP "dotnet-install-$([guid]::NewGuid()).ps1"
try {
    Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile $installScript
    $installArgs = @('-InstallDir', $installDir)
    if ($version) {
        $installArgs += @('-Version', $version)
        $rollForward = 'disable'
    } else {
        $installArgs += @('-Channel', $channel, '-Quality', $quality)
    }
    & $installScript @installArgs
}
finally {
    if (Test-Path -LiteralPath $installScript) {
        Remove-Item -LiteralPath $installScript -Force
    }
}
$sdkVersion = & "$installDir\\dotnet.exe" --version
$globalJson = if (Test-Path 'global.json') {
    Copy-Item 'global.json' 'global.json.bak'
    Get-Content -Path 'global.json' -Raw | ConvertFrom-Json
} else {
    [pscustomobject]@{}
}
if (-not $globalJson.PSObject.Properties['sdk']) {
    $globalJson | Add-Member -MemberType NoteProperty -Name 'sdk' -Value ([pscustomobject]@{})
}
$updates = [ordered]@{
    version = $sdkVersion
    allowPrerelease = $allowPrerelease
    rollForward = $rollForward
    paths = @('.dotnet', '$host$')
    errorMessage = $errorMessage
}
foreach ($entry in $updates.GetEnumerator()) {
    $property = $globalJson.sdk.PSObject.Properties[$entry.Key]
    if ($property) {
        $property.Value = $entry.Value
    } else {
        $globalJson.sdk | Add-Member -MemberType NoteProperty -Name $entry.Key -Value $entry.Value
    }
}
$globalJson | ConvertTo-Json -Depth 10 | Set-Content -Path 'global.json' -Encoding UTF8
if (-not (Test-Path .gitignore) -or -not (Select-String -Path .gitignore -Pattern '^\\.dotnet/$' -Quiet)) {
    Add-Content -Path .gitignore -Value '.dotnet/'
}
if ($Workloads.Count -gt 0) { & "$installDir\\dotnet.exe" workload install @Workloads }
Write-Host "Done. SDK: $sdkVersion"
将这些脚本提交到代码仓库,以便团队成员运行。

Step 11 — Verify SDK resolution

步骤11 — 验证SDK解析

bash
dotnet --version
Output should match the locally installed version. If not, check: global.json location,
paths
array contents, host dotnet version ≥ 10.
bash
dotnet --version
输出应与本地安装的版本一致。若不一致,请检查:global.json的位置、
paths
数组内容、宿主dotnet版本是否≥10。

Step 12 — Summarize and explain cleanup

步骤12 — 总结并说明清理方法

Tell the user: SDK installed, global.json configured, .dotnet/ gitignored, system install untouched. Cleanup: delete
.dotnet/
, remove
paths
/
errorMessage
from global.json, optionally delete install scripts. Include the final
global.json
sdk
values (or a short snippet) so the user can see the configured version,
paths
, and any
errorMessage
. If workloads were requested, include the local
dotnet workload install ...
command used and the workload verification result or the exact blocker if the workload could not be installed.
告知用户:SDK已安装、global.json已配置、.dotnet/已加入git忽略、系统级安装未受影响。清理方法:删除
.dotnet/
目录,从global.json中移除
paths
/
errorMessage
配置,可选择删除安装脚本。附上最终的global.json的
sdk
配置值(或简短代码片段),以便用户查看已配置的版本、
paths
及任何
errorMessage
。若用户要求安装工作负载,需附上使用的本地
dotnet workload install ...
命令及工作负载验证结果,若安装失败则说明具体障碍。

Common pitfalls

常见问题

PitfallCauseFix
paths
ignored
Host
dotnet
< v10
Install .NET 10+ system-wide
Wrong SDK resolves
global.json
in parent directory
Check for global.json up the tree
Teammates get "SDK not found"
.dotnet/
gitignored, no install script run
Use
errorMessage
in global.json
Workloads missingUsed system
dotnet
instead of local
Use
./.dotnet/dotnet workload install
dotnet app.dll
wrong runtime
paths
is SDK-only, not apphost
Use
dotnet run
or set
DOTNET_ROOT
问题原因解决方法
paths
功能被忽略
宿主
dotnet
版本<v10
全局安装.NET 10及以上版本
解析到错误的SDK父目录存在global.json检查上级目录是否有global.json
团队成员提示“SDK未找到”
.dotnet/
已加入git忽略,且未运行安装脚本
在global.json中添加
errorMessage
配置
工作负载缺失使用了系统级
dotnet
而非本地版本
使用
./.dotnet/dotnet workload install
命令
dotnet app.dll
使用错误运行时
paths
仅适用于SDK,不适用于应用宿主
使用
dotnet run
命令或设置
DOTNET_ROOT
环境变量