-
Notifications
You must be signed in to change notification settings - Fork 77
Add setup-local-sdk skill for global.json paths feature #508
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jfversluis
wants to merge
7
commits into
main
Choose a base branch
from
skills/setup-local-sdk
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+321
−0
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
a027331
Add setup-local-sdk skill for global.json paths feature
jfversluis a3f3e70
Address PR review feedback
jfversluis 47df259
Trim SKILL.md to 500 lines, condense notes
jfversluis 528b5a2
Address second review round and improve eval
jfversluis 31252f0
Major rewrite: halve skill size, improve activation and eval
jfversluis 64f7786
Address review round 4: cross-platform fixes and conditional configs
jfversluis ebbec97
Fix timeouts, review comments, and eval issues
jfversluis File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,252 @@ | ||
| --- | ||
| name: setup-local-sdk | ||
| description: > | ||
| Install a .NET SDK locally for safe preview testing, specific-version pinning, or | ||
| reproducible team setups — without modifying the system-wide installation. | ||
| USE FOR: trying .NET previews safely, testing specific SDK versions, installing MAUI | ||
| or other workloads on a preview, updating or replacing an existing local SDK, | ||
| creating reproducible team/CI install scripts, configuring global.json paths. | ||
| DO NOT USE FOR: system-wide SDK installs, .NET hosts older than 10, runtime-only | ||
| installs, or projects not using SDK-style commands. | ||
| --- | ||
|
|
||
| # setup-local-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`). | ||
|
|
||
| ## 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. | ||
|
|
||
| ## Inputs / Prerequisites | ||
|
|
||
| | Input | Required | Default | Notes | | ||
| |---|---|---|---| | ||
| | Channel or version | No | `11.0` | e.g. `11.0`, `STS`, `LTS`, or an exact version like `11.0.100-preview.2.26159.112` | | ||
| | Quality | No | `preview` | One of: `daily`, `preview`, `GA` | | ||
|
|
||
| ### Prerequisites | ||
|
|
||
| 1. **A .NET 10+ SDK is installed globally** — run `dotnet --version`; major ≥ 10. | ||
| 2. **curl** (macOS/Linux) or **PowerShell** (Windows) is available. | ||
|
|
||
| ## Workflow | ||
|
|
||
| ### Step 1 — Clarify what to install | ||
|
|
||
| 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. | ||
|
|
||
| ### Step 2 — Verify .NET 10+ host | ||
|
|
||
| ```bash | ||
| dotnet --version | ||
| ``` | ||
|
|
||
| If major version < 10, stop: the `paths` feature requires .NET 10+. | ||
|
|
||
| ### Step 3 — Detect operating system | ||
|
|
||
| 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`. | ||
|
|
||
| ### Step 4 — Check for existing local SDK | ||
|
|
||
| ```bash | ||
| test -d .dotnet && echo "exists" || echo "not found" | ||
| ``` | ||
|
|
||
| If `.dotnet/` exists, ask: update with the new version, or skip and keep it? | ||
|
|
||
| ### Step 5 — Download and run the install script | ||
|
|
||
| **macOS / Linux:** | ||
|
|
||
| ```bash | ||
| curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh | ||
| bash /tmp/dotnet-install.sh --channel <CHANNEL> --quality <QUALITY> --install-dir .dotnet | ||
| ``` | ||
|
|
||
| **Windows (PowerShell):** | ||
|
|
||
| ```powershell | ||
| Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile "$env:TEMP\dotnet-install.ps1" | ||
| & "$env:TEMP\dotnet-install.ps1" -Channel <CHANNEL> -Quality <QUALITY> -InstallDir .dotnet | ||
| ``` | ||
|
|
||
| For exact versions: use `--version <VERSION>` (bash) or `-Version <VERSION>` (PowerShell) | ||
| instead of channel/quality flags. The install script is from Microsoft's official | ||
| URL (https://dot.net/v1/dotnet-install.sh). | ||
|
|
||
| ### Step 6 — Identify the installed version | ||
|
|
||
| ```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`. | ||
|
|
||
| ### Step 7 — Install workloads (if requested) | ||
|
|
||
| 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`). | ||
|
|
||
| > **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](https://github.com/dotnet/sdk/issues/49825).) | ||
|
|
||
| ### Step 8 — Create or update 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"`: rolls forward across feature bands, not just patches. | ||
| - `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$"]}}` | ||
|
|
||
| ### Step 9 — Update .gitignore | ||
|
|
||
| **macOS / Linux (or Git Bash):** | ||
|
|
||
| ```bash | ||
| grep -qxF '.dotnet/' .gitignore 2>/dev/null || echo '.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 10 — Create team install scripts | ||
|
|
||
| Create if user mentioned "team", "share", "CI", "scripts", etc. Otherwise offer. | ||
|
|
||
| **install-dotnet.sh:** | ||
|
|
||
| ```bash | ||
| #!/usr/bin/env bash | ||
| set -euo pipefail | ||
| INSTALL_DIR=".dotnet" | ||
| CHANNEL="11.0" | ||
| QUALITY="preview" | ||
| WORKLOADS=("${@}") | ||
| curl -fsSL https://dot.net/v1/dotnet-install.sh -o /tmp/dotnet-install.sh | ||
| bash /tmp/dotnet-install.sh --channel "$CHANNEL" --quality "$QUALITY" --install-dir "$INSTALL_DIR" | ||
| SDK_VERSION=$("$INSTALL_DIR/dotnet" --version) | ||
| [ -f global.json ] && cp global.json global.json.bak | ||
| cat > global.json <<EOF | ||
| { | ||
| "sdk": { | ||
| "version": "$SDK_VERSION", | ||
| "allowPrerelease": true, | ||
| "rollForward": "latestFeature", | ||
| "paths": [".dotnet", "\$host\$"], | ||
| "errorMessage": "Required .NET SDK not found. Run ./install-dotnet.sh (or .ps1) to install it locally." | ||
| } | ||
| } | ||
| EOF | ||
| grep -qxF '.dotnet/' .gitignore 2>/dev/null || echo '.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' | ||
| Invoke-WebRequest -Uri 'https://dot.net/v1/dotnet-install.ps1' -OutFile "$env:TEMP\dotnet-install.ps1" | ||
| & "$env:TEMP\dotnet-install.ps1" -Channel $channel -Quality $quality -InstallDir $installDir | ||
| $sdkVersion = & "$installDir\dotnet.exe" --version | ||
| if (Test-Path 'global.json') { Copy-Item 'global.json' 'global.json.bak' } | ||
| @" | ||
| { | ||
| "sdk": { | ||
| "version": "$sdkVersion", | ||
| "allowPrerelease": true, | ||
| "rollForward": "latestFeature", | ||
| "paths": [".dotnet", "`$host`$"], | ||
| "errorMessage": "Required .NET SDK not found. Run ./install-dotnet.sh (or .ps1) to install it locally." | ||
| } | ||
| } | ||
| "@ | 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. | ||
|
|
||
| ### Step 11 — Verify SDK resolution | ||
|
|
||
| ```bash | ||
| dotnet --version | ||
| ``` | ||
|
|
||
| Output should match the locally installed version. If not, check: global.json | ||
| location, `paths` array contents, host dotnet version ≥ 10. | ||
|
|
||
| ### Step 12 — Summarize and explain cleanup | ||
|
|
||
| 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. | ||
|
|
||
| ## Common pitfalls | ||
|
|
||
| | Pitfall | Cause | Fix | | ||
| |---|---|---| | ||
| | `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 missing | Used 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` | | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| scenarios: | ||
| - name: "Basic local SDK setup with .NET 11 preview" | ||
| prompt: "I want to try the latest .NET 11 preview in my project without affecting my global install. Set it up locally for me." | ||
| assertions: | ||
| - type: "output_contains" | ||
| value: "dotnet-install" | ||
| - type: "output_contains" | ||
| value: "global.json" | ||
| - type: "output_contains" | ||
| value: ".dotnet" | ||
| rubric: | ||
| - "The agent installed the SDK into a .dotnet/ directory using the dotnet-install script" | ||
| - "The agent created or updated global.json with a paths array" | ||
| - "The agent added .dotnet/ to .gitignore" | ||
| - "The agent explained how to clean up" | ||
| timeout: 300 | ||
|
|
||
| - name: "Install a specific SDK version locally" | ||
| prompt: "Install .NET SDK version 11.0.100-preview.2.26159.112 locally using global.json paths." | ||
| assertions: | ||
| - type: "output_contains" | ||
| value: "11.0.100-preview.2.26159.112" | ||
| - type: "output_contains" | ||
| value: "global.json" | ||
| rubric: | ||
| - "The agent used a version flag instead of channel/quality to install the exact version" | ||
| - "The agent configured global.json with the paths array and exact version" | ||
| - "The agent verified the installed version matches the request" | ||
| timeout: 300 | ||
|
|
||
| - name: "Set up local SDK with MAUI workload" | ||
| prompt: "I need to test my .NET MAUI app with the latest .NET 11 preview. Set up a local SDK with the MAUI workload installed." | ||
| assertions: | ||
| - type: "output_contains" | ||
| value: "workload" | ||
| - type: "output_contains" | ||
| value: ".dotnet" | ||
| rubric: | ||
| - "The agent installed the SDK locally and the MAUI workload" | ||
| - "The agent used ./.dotnet/dotnet (not system dotnet) for workload commands" | ||
| - "The agent configured global.json with paths" | ||
| timeout: 300 | ||
|
|
||
| - name: "Create team install scripts" | ||
| prompt: "Set up a local .NET 11 preview SDK and create install scripts so teammates can reproduce the setup on any OS." | ||
| assertions: | ||
| - type: "output_contains" | ||
| value: "install-dotnet" | ||
| - type: "output_contains" | ||
| value: "global.json" | ||
| rubric: | ||
| - "The agent created install-dotnet.sh and install-dotnet.ps1 scripts" | ||
| - "The agent configured global.json with an errorMessage directing to the scripts" | ||
| - "The agent explained that teammates run the script after cloning" | ||
| timeout: 300 | ||
|
|
||
| - name: "Detect incompatible .NET host version" | ||
| prompt: "Set up a local .NET 11 preview SDK for my project. My dotnet --version shows 9.0.306." | ||
| assertions: | ||
| - type: "output_contains" | ||
| value: ".NET 10" | ||
| rubric: | ||
| - "The agent detected that version 9.0.306 is below the .NET 10 requirement" | ||
| - "The agent explained that .NET 10+ is required for the paths feature" | ||
| - "The agent told the user to install .NET 10+ system-wide first" | ||
| timeout: 120 |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.