关于 setup-local-sdk
指导开发者将 .NET SDK 安装到项目本地的 `.dotnet/`,并配置 `global.json` 选择该 SDK,不改动系统级安装,同时说明验证与清理步骤。
主要功能
- 01项目本地 SDK 安装
- 02配置 `global.json` paths
- 03检查宿主版本前提
- 04按平台提供安装步骤
- 05按需安装 workloads
- 06生成团队安装脚本
适用场景
- 01为单个项目隔离 SDK
- 02配置预览版或指定版本 SDK
- 03准备可复用的团队或 CI 配置
- 04删除本地 SDK 以撤销设置
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dotnet/ skills --skill setup-local-sdk安装后在 Claude Code 中输入/setup-local-sdk确认已加载。
详细介绍
setup-local-sdk
用途
指导用户将 .NET SDK 安装到项目本地的 .dotnet/ 目录,并通过 global.json 的 paths 功能(.NET 10+)进行配置。
示例使用 .NET 11,但此方法适用于任何版本,无论是预发行版还是稳定版。
最终得到的是完全隔离的 SDK,它:
- 不会修改系统范围的 .NET 安装。
- 从项目根目录运行
dotnet命令时会自动使用。 - 可以删除以恢复原状(
rm -rf .dotnet/或Remove-Item -Recurse -Force .\.dotnet)。
不适用场景
- 用户需要系统范围安装——请引导其使用官方安装程序。
- 主机上的
dotnet版本低于 v10——不存在paths功能;请说明并停止。 - 用户只需要安装运行时——
paths仅适用于 SDK 解析。
输入 / 前提条件
| 输入 | 必需 | 默认值 | 备注 |
|---|---|---|---|
| 通道或版本 | 否 | 11.0 | 例如:11.0、STS、LTS,或类似 11.0.100-preview.2.26159.112 的确切版本 |
| 质量 | 否 | preview | 可选值:daily、preview、ga |
| jq | 否 | — | 使用 bash 团队脚本修补现有 global.json 时可选;如果没有 jq,请勿覆盖该文件 |
前提条件
- 已全局安装 .NET 10+ SDK——运行
dotnet --version;主版本号需 ≥ 10。 - 可使用 curl(macOS/Linux)或 PowerShell(Windows)。
工作流
步骤 1 — 确认要安装的版本
如果用户未指定,请询问其需要哪个 .NET SDK 版本(例如“最新的 .NET 11 预览版”或类似 11.0.100-preview.2.26159.112 的确切版本)。
将回答映射为 --channel/--quality 或 --version 标志。
步骤 2 — 验证 .NET 10+ 主机
如果用户已提供 dotnet --version 的输出,请将其视为该用户机器上版本的权威信息。不要用 Agent 工作区中的版本覆盖;如果两者不同,请说明工作区中的版本不同,并继续针对用户的机器提供指导。
dotnet --version
如果主版本号 < 10,请在下载任何内容之前停止:paths 功能需要 .NET 10+ 主机 SDK。告知用户先在系统范围内安装 .NET 10 或更高版本,然后再继续设置本地 SDK。
步骤 3 — 检测操作系统
运行 uname -s 2>/dev/null。如果成功(包括 MINGW*、MSYS*、CYGWIN*——这些都是支持 bash 的环境,例如 Git Bash),则使用 bash/dotnet-install.sh。
如果失败(未安装 Git Bash 的原生 Windows 环境),则使用 PowerShell/dotnet-install.ps1。
步骤 4 — 检查是否已有本地 SDK
macOS / Linux:
test -d .dotnet && echo "exists" || echo "not found"
Windows(PowerShell):
if (Test-Path -LiteralPath .\.dotnet) { "exists" } else { "not found" }
如果 .dotnet/ 已存在,请询问:要更新到新版本,还是跳过并保留现有版本?
步骤 5 — 下载并运行安装脚本
macOS / Linux:
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):
$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),而不是通道/质量标志。安装脚本来自 Microsoft 官方 URL:https://dot.net/v1/dotnet-install.sh 和 https://dot.net/v1/dotnet-install.ps1。
步骤 6 — 确认已安装的版本
./.dotnet/dotnet --version # macOS/Linux
.\.dotnet\dotnet.exe --version # Windows
记录确切的版本字符串(例如 11.0.100-preview.2.26159.112),以便写入 global.json。
步骤 7 — 创建或更新 global.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$作为系统级回退。rollForward: "latestFeature":用于安装最新预览版或浮动功能带版本。- 精确版本请求:使用
rollForward: "disable",以免 SDK 解析切换到其他功能带。 allowPrerelease:仅在安装预发布版 SDK 时设为true。稳定版本请省略。errorMessage:仅在创建团队安装脚本时添加(步骤 10)。否则请省略。
如果 global.json 已存在,请谨慎合并:保留现有属性(如 msbuild-sdks、
tools 等),只添加或更新 sdk 部分。先读取现有文件,更新或添加
大模型 对象,然后写回文件。这样可以确保不会丢失跨项目配置(例如 MSBuild 设置)。
修改前务必备份原始文件(例如 global.json.bak)。
最简配置(无需固定版本时):
{"sdk":{"paths":[".dotnet","$host$"]}}
步骤 8 — 更新 .gitignore
macOS / Linux(或 Git Bash):
grep -qxF '.dotnet/' .gitignore 2>/dev/null || printf '\n.dotnet/\n' >> .gitignore
Windows(PowerShell):
if (-not (Test-Path .gitignore) -or -not (Select-String -Path .gitignore -Pattern '^\.dotnet/$' -Quiet)) {
Add-Content -Path .gitignore -Value '.dotnet/'
}
步骤 9 — 安装工作负载(如有要求)
仅在完成 global.json 和 .gitignore 后执行此操作,避免耗时较长或受平台限制的
工作负载安装影响基础本地 SDK 配置的可用性。
如果用户提到了 MAUI、移动端、工作负载、Blazor WASM 或跨平台, 请使用本地二进制文件安装(无需 sudo):
./.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。)
步骤 10 — 创建团队安装脚本
如果用户提到“团队”“共享”“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:
#!/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"
chmod +x install-dotnet.sh
install-dotnet.ps1:
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"
将这些脚本提交到代码库,以便团队成员运行。
步骤 11 — 验证 SDK 解析
dotnet --version
输出应与本地安装的版本一致。如果不一致,请检查:global.json
的位置、paths 数组的内容、宿主 dotnet 版本是否 ≥ 10。
步骤 12 — 总结并说明清理方式
告诉用户:SDK 已安装,global.json 已配置,.dotnet/ 已加入 gitignore,未更改系统安装。清理方式:删除 .dotnet/,从 global.json 中移除 paths/errorMessage,也可以选择删除安装脚本。附上最终的 global.json sdk 值(或简短代码片段),以便用户查看已配置的版本、paths 和任何 errorMessage。如果用户要求安装工作负载,请附上所用的本地 dotnet workload install ... 命令,以及工作负载验证结果;如果无法安装工作负载,则说明具体阻碍。
常见问题
| 问题 | 原因 | 解决方法 |
|---|---|---|
忽略了 paths | 主机上的 dotnet < v10 | 在系统范围内安装 .NET 10+ |
| 解析到错误的 SDK | 父目录中存在 global.json | 检查上层目录中的 global.json |
| 队友遇到“找不到 SDK” | .dotnet/ 已加入 gitignore,且未运行安装脚本 | 在 global.json 中使用 errorMessage |
| 缺少工作负载 | 使用了系统的 dotnet,而非本地版本 | 使用 ./.dotnet/dotnet workload install |
dotnet app.dll 使用了错误的运行时 | paths 仅适用于 SDK,不适用于 apphost | 使用 dotnet run 或设置 DOTNET_ROOT |