.NET SDK Setup Check
Verify the .NET SDK is installed and optionally auto-install it
Metadata
- Author: ropean, Claude Sonnet (Anthropic)
- Version: 1.0.0
Code
python
#!/usr/bin/env python3
"""
@title .NET SDK Setup Check
@description Verify the .NET SDK is installed and optionally auto-install it
@author ropean, Claude Sonnet (Anthropic)
@version 1.0.0
Verify that the .NET SDK is installed and accessible on PATH.
If `dotnet` is not found or the version check fails, prints actionable
installation instructions for every major platform and exits with code 1.
With --install, automatically downloads and installs the recommended SDK
version and configures the shell PATH.
Exit codes:
0 - dotnet CLI is available
1 - dotnet CLI is missing or broken
@example
Usage example:
python dotnet-setup.py # check only
python dotnet-setup.py --install # auto-install if missing
"""
import argparse
import os
import shutil
import subprocess
import sys
# ── Constants ──────────────────────────────────────────────────────────────────
RECOMMENDED_MAJOR = "8"
DOWNLOAD_URL = f"https://dotnet.microsoft.com/en-us/download/dotnet/{RECOMMENDED_MAJOR}.0"
PS_INSTALL_SCRIPT = "https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.ps1"
BASH_INSTALL_SCRIPT = "https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.sh"
# ── Colored output helpers ─────────────────────────────────────────────────────
_NO_COLOR = not sys.stdout.isatty()
def _colored(text: str, code: str) -> str:
if _NO_COLOR:
return text
return f"\033[{code}m{text}\033[0m"
def green(text: str) -> str:
return _colored(text, "32")
def yellow(text: str) -> str:
return _colored(text, "33")
def red(text: str) -> str:
return _colored(text, "31")
def cyan(text: str) -> str:
return _colored(text, "36")
def dim(text: str) -> str:
return _colored(text, "90")
# ── Core logic ─────────────────────────────────────────────────────────────────
def check_dotnet() -> bool:
"""Return True if `dotnet --version` succeeds."""
if not shutil.which("dotnet"):
return False
try:
result = subprocess.run(
["dotnet", "--version"],
capture_output=True,
text=True,
timeout=15,
)
return result.returncode == 0 and result.stdout.strip() != ""
except (OSError, subprocess.TimeoutExpired):
return False
def get_dotnet_version() -> str:
result = subprocess.run(
["dotnet", "--version"],
capture_output=True,
text=True,
timeout=15,
)
return result.stdout.strip()
def print_install_instructions() -> None:
is_windows = sys.platform == "win32"
is_mac = sys.platform == "darwin"
print()
print(red("ERROR: 'dotnet' CLI is not installed or not on PATH."))
print()
print(f" .NET {RECOMMENDED_MAJOR} (recommended) is required. Install using one of these methods:")
print()
idx = 1
print(f" {cyan(f'{idx}.')} Download installer:")
print(f" {DOWNLOAD_URL}")
print()
idx += 1
if is_windows:
print(f" {cyan(f'{idx}.')} PowerShell:")
print(f" Invoke-WebRequest '{PS_INSTALL_SCRIPT}' -OutFile dotnet-install.ps1")
print(f" ./dotnet-install.ps1 -Channel {RECOMMENDED_MAJOR}.0")
print()
idx += 1
print(f" {cyan(f'{idx}.')} Bash:")
print(f" curl -fsSL {BASH_INSTALL_SCRIPT} | bash /dev/stdin --channel {RECOMMENDED_MAJOR}.0")
print()
idx += 1
if is_mac:
print(f" {cyan(f'{idx}.')} Homebrew:")
print(f" brew install dotnet@{RECOMMENDED_MAJOR}")
print()
print(dim(" After installation, restart your terminal and re-run this script."))
print()
# ── Auto-install ───────────────────────────────────────────────────────────────
DOTNET_INSTALL_DIR = os.path.join(os.path.expanduser("~"), ".dotnet")
def _shell_profile_path() -> str | None:
"""Return the most appropriate shell profile for the current user."""
home = os.path.expanduser("~")
shell = os.environ.get("SHELL", "")
if "zsh" in shell:
candidates = [".zshrc", ".zprofile"]
elif "bash" in shell:
candidates = [".bashrc", ".bash_profile", ".profile"]
else:
candidates = [".profile"]
for name in candidates:
path = os.path.join(home, name)
if os.path.isfile(path):
return path
# Fallback: create the first candidate
return os.path.join(home, candidates[0])
def _path_already_configured(profile_path: str) -> bool:
"""Check if .dotnet is already exported in the shell profile."""
if not os.path.isfile(profile_path):
return False
try:
with open(profile_path, encoding="utf-8", errors="replace") as f:
content = f.read()
return ".dotnet" in content and "PATH" in content
except OSError:
return False
def _add_path_to_profile(profile_path: str) -> bool:
"""Append dotnet PATH export to the shell profile. Return True on success."""
export_block = (
'\n# .NET SDK\n'
f'export DOTNET_ROOT="{DOTNET_INSTALL_DIR}"\n'
f'export PATH="$DOTNET_ROOT:$PATH"\n'
)
try:
with open(profile_path, "a", encoding="utf-8") as f:
f.write(export_block)
return True
except OSError as e:
print(red(f" Failed to update {profile_path}: {e}"))
return False
def install_dotnet() -> int:
"""Download and install .NET SDK, then configure PATH."""
is_windows = sys.platform == "win32"
print()
print(cyan(f" Installing .NET {RECOMMENDED_MAJOR}.0 SDK..."))
print()
if is_windows:
# Download and run PowerShell install script
ps_local = os.path.join(os.environ.get("TEMP", "."), "dotnet-install.ps1")
print(dim(f" Downloading install script to {ps_local}..."))
dl = subprocess.run(
["powershell", "-Command",
f"Invoke-WebRequest '{PS_INSTALL_SCRIPT}' -OutFile '{ps_local}' -UseBasicParsing"],
capture_output=True, text=True, timeout=60,
)
if dl.returncode != 0:
print(red(f" Failed to download install script: {dl.stderr.strip()}"))
return 1
print(dim(f" Running install script (channel {RECOMMENDED_MAJOR}.0)..."))
install = subprocess.run(
["powershell", "-ExecutionPolicy", "Bypass", "-File", ps_local,
"-Channel", f"{RECOMMENDED_MAJOR}.0"],
timeout=300,
)
else:
# Download and run Bash install script
print(dim(f" Downloading and running install script (channel {RECOMMENDED_MAJOR}.0)..."))
curl = subprocess.run(
["curl", "-fsSL", BASH_INSTALL_SCRIPT],
capture_output=True, timeout=60,
)
if curl.returncode != 0:
print(red(" Failed to download install script."))
return 1
install = subprocess.run(
["bash", "/dev/stdin", "--channel", f"{RECOMMENDED_MAJOR}.0"],
input=curl.stdout, timeout=300,
)
if install.returncode != 0:
print(red(" Installation failed. See output above."))
return 1
# Verify the binary exists
dotnet_bin = os.path.join(DOTNET_INSTALL_DIR, "dotnet")
if is_windows:
dotnet_bin += ".exe"
if not os.path.isfile(dotnet_bin):
print(red(f" Expected binary not found: {dotnet_bin}"))
return 1
# Get installed version
ver_result = subprocess.run(
[dotnet_bin, "--version"], capture_output=True, text=True, timeout=15,
)
version = ver_result.stdout.strip() if ver_result.returncode == 0 else "unknown"
print(green(f" Installed .NET SDK v{version} to {DOTNET_INSTALL_DIR}"))
# Configure PATH
if is_windows:
print()
print(yellow(" To add dotnet to your PATH permanently on Windows, run:"))
print(f' [Environment]::SetEnvironmentVariable("PATH", "{DOTNET_INSTALL_DIR};$env:PATH", "User")')
print()
else:
profile = _shell_profile_path()
if profile and not _path_already_configured(profile):
if _add_path_to_profile(profile):
print(green(f" Added dotnet to PATH in {profile}"))
print(dim(" Run the following to apply now, or restart your terminal:"))
print(dim(f" source {profile}"))
else:
print(yellow(f" Could not update {profile}. Add manually:"))
print(f' export PATH="{DOTNET_INSTALL_DIR}:$PATH"')
elif profile:
print(dim(f" PATH already configured in {profile}"))
else:
print(yellow(" Add this to your shell profile:"))
print(f' export PATH="{DOTNET_INSTALL_DIR}:$PATH"')
print()
return 0
# ── Main ───────────────────────────────────────────────────────────────────────
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
description="Check (and optionally install) the .NET SDK.",
)
parser.add_argument(
"--install", action="store_true",
help=f"Auto-install .NET {RECOMMENDED_MAJOR} SDK and configure PATH",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
print()
print(cyan("=== .NET SDK Setup Check ==="))
print()
if check_dotnet():
version = get_dotnet_version()
print(green(f" dotnet CLI is available: v{version}"))
major = version.split(".")[0]
if major != RECOMMENDED_MAJOR:
print(yellow(f" Note: Recommended version is .NET {RECOMMENDED_MAJOR}.x, you have {version}."))
print(yellow(f" This may still work, but consider upgrading: {DOWNLOAD_URL}"))
print()
return 0
if args.install:
return install_dotnet()
print_install_instructions()
print(dim(f" Or run this script with --install to auto-install .NET {RECOMMENDED_MAJOR}:"))
print(dim(f" python3 {os.path.basename(__file__)} --install"))
print()
return 1
if __name__ == "__main__":
sys.exit(main())File Information
- Filename:
dotnet-setup.py - Category: python
- Language: PYTHON