Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 107 additions & 0 deletions .claude/hooks/install-dotnet.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/bin/bash
# Installs the .NET SDK into ~/.dotnet for Claude Code cloud sessions.
#
# The SDK is unpacked from the layers of the official
# mcr.microsoft.com/dotnet/sdk container image, each checked against the
# sha256 digest its manifest names. dotnet-install.sh is not used: it comes from
# builds.dotnet.microsoft.com, which the default cloud network policy does not
# allow, and it is a script fetched and run with nothing to check it against.
# Ubuntu's own dotnet-sdk-10.0 package is the 1xx feature band, whose compiler
# is too old for ktsu.Sdk.Analyzers (CS9057).
#
# Usage: install-dotnet.sh <channel> <minimum sdk version> [runtime channel...]
# e.g. install-dotnet.sh 10.0 10.0.400 8.0 9.0
# The extra runtime channels are for test projects that multi-target older
# frameworks: building them needs only the SDK, running them needs the runtime.
set -euo pipefail

channel="${1:-10.0}"
minimum="${2:-${channel}.100}"
shift 2 || shift $#
runtimes=("$@")
root="${DOTNET_INSTALL_DIR:-$HOME/.dotnet}"

version_at_least() {
[ "$(printf '%s\n%s\n' "$2" "$1" | sort -V | head -n1)" = "$2" ]

Check warning on line 25 in .claude/hooks/install-dotnet.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Assign this positional parameter to a local variable.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsZayYDpVM_D-meL&open=AaFZSsZayYDpVM_D-meL&pullRequest=145

Check failure on line 25 in .claude/hooks/install-dotnet.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsZayYDpVM_D-meJ&open=AaFZSsZayYDpVM_D-meJ&pullRequest=145

Check warning on line 25 in .claude/hooks/install-dotnet.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Assign this positional parameter to a local variable.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsZayYDpVM_D-meM&open=AaFZSsZayYDpVM_D-meM&pullRequest=145

Check warning on line 25 in .claude/hooks/install-dotnet.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Assign this positional parameter to a local variable.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsZayYDpVM_D-meK&open=AaFZSsZayYDpVM_D-meK&pullRequest=145
}

has_runtime() {
[ -x "$root/dotnet" ] && "$root/dotnet" --list-runtimes 2>/dev/null | grep -q "^Microsoft.NETCore.App $1\."

Check failure on line 29 in .claude/hooks/install-dotnet.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsZayYDpVM_D-meN&open=AaFZSsZayYDpVM_D-meN&pullRequest=145

Check warning on line 29 in .claude/hooks/install-dotnet.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Assign this positional parameter to a local variable.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsZayYDpVM_D-meO&open=AaFZSsZayYDpVM_D-meO&pullRequest=145
}

has_sdk() {
[ -x "$root/dotnet" ] || return 1

Check failure on line 33 in .claude/hooks/install-dotnet.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsZayYDpVM_D-meP&open=AaFZSsZayYDpVM_D-meP&pullRequest=145
local sdk
for sdk in $("$root/dotnet" --list-sdks 2>/dev/null | awk '{print $1}'); do
case "$sdk" in

Check failure on line 36 in .claude/hooks/install-dotnet.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Add a default case (*) to handle unexpected values.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsZayYDpVM_D-meQ&open=AaFZSsZayYDpVM_D-meQ&pullRequest=145
"$channel".*) version_at_least "$sdk" "$minimum" && return 0 ;;
esac
done
return 1
}

# install_from_container_image <image> <tag>, e.g. sdk 10.0 or runtime 8.0
install_from_container_image() {
local registry="https://mcr.microsoft.com/v2/dotnet/$1"
local arch
case "$(uname -m)" in
x86_64) arch=amd64 ;;
aarch64 | arm64) arch=arm64 ;;
*) echo "install-dotnet: unsupported architecture $(uname -m)" >&2; return 1 ;;
esac

local index manifest digest layer work
index="$(curl -fsSL -H 'Accept: application/vnd.oci.image.index.v1+json,application/vnd.docker.distribution.manifest.list.v2+json' \
"$registry/manifests/$2-noble")"

Check warning on line 55 in .claude/hooks/install-dotnet.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Assign this positional parameter to a local variable.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsZayYDpVM_D-meR&open=AaFZSsZayYDpVM_D-meR&pullRequest=145

Check warning on line 55 in .claude/hooks/install-dotnet.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Not enforcing HTTPS here might allow for redirections to insecure websites. Make sure it is safe here.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsZayYDpVM_D-meG&open=AaFZSsZayYDpVM_D-meG&pullRequest=145
digest="$(jq -r --arg arch "$arch" \
'.manifests[] | select(.platform.os == "linux" and .platform.architecture == $arch and (.platform.variant // "") == "") | .digest' \
<<<"$index" | head -n1)"
manifest="$(curl -fsSL -H 'Accept: application/vnd.oci.image.manifest.v1+json,application/vnd.docker.distribution.manifest.v2+json' \
"$registry/manifests/$digest")"

Check warning on line 60 in .claude/hooks/install-dotnet.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Not enforcing HTTPS here might allow for redirections to insecure websites. Make sure it is safe here.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsZayYDpVM_D-meH&open=AaFZSsZayYDpVM_D-meH&pullRequest=145

# A runtime image contributes its shared framework and nothing else, so its
# older dotnet host never replaces the one the SDK brought.
local wanted='usr/share/dotnet/*'
[ "$1" = sdk ] || wanted='usr/share/dotnet/shared/*'

Check failure on line 65 in .claude/hooks/install-dotnet.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsZayYDpVM_D-meS&open=AaFZSsZayYDpVM_D-meS&pullRequest=145

Check warning on line 65 in .claude/hooks/install-dotnet.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Assign this positional parameter to a local variable.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsZayYDpVM_D-meT&open=AaFZSsZayYDpVM_D-meT&pullRequest=145

work="$(mktemp -d)"
mkdir -p "$root"
# Only the files under usr/share/dotnet are taken: the runtime, ASP.NET Core
# and SDK layers. The rest of the image is its operating system. Layers are
# extracted over what is there, so an SDK and older runtimes share one root.
# Each layer is checked against the sha256 digest its manifest names before
# anything in it is unpacked, since what it contains is run as dotnet.
while IFS= read -r layer; do
case "$layer" in
sha256:*) ;;
*) echo "install-dotnet: unsupported layer digest $layer" >&2; rm -rf "$work"; return 1 ;;
esac
curl -fsSL -o "$work/layer.tgz" "$registry/blobs/$layer"

Check warning on line 79 in .claude/hooks/install-dotnet.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Not enforcing HTTPS here might allow for redirections to insecure websites. Make sure it is safe here.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsZayYDpVM_D-meI&open=AaFZSsZayYDpVM_D-meI&pullRequest=145
if [ "sha256:$(sha256sum "$work/layer.tgz" | awk '{print $1}')" != "$layer" ]; then

Check failure on line 80 in .claude/hooks/install-dotnet.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsZayYDpVM_D-meU&open=AaFZSsZayYDpVM_D-meU&pullRequest=145
echo "install-dotnet: layer $layer does not match its digest" >&2
rm -rf "$work"
return 1
fi
tar -xzf "$work/layer.tgz" -C "$root" --strip-components=3 --wildcards "$wanted" 2>/dev/null || true
done < <(jq -r '.layers[].digest' <<<"$manifest")
rm -rf "$work"
}

if ! has_sdk; then
echo "install-dotnet: installing the .NET $channel SDK (at least $minimum) into $root" >&2
install_from_container_image sdk "$channel"
if ! has_sdk; then
echo "install-dotnet: no .NET $channel SDK at or above $minimum after installing" >&2
exit 1
fi
fi

for runtime in "${runtimes[@]}"; do
has_runtime "$runtime" && continue
echo "install-dotnet: installing the .NET $runtime runtime into $root" >&2
install_from_container_image runtime "$runtime"
if ! has_runtime "$runtime"; then
echo "install-dotnet: no .NET $runtime runtime after installing" >&2
exit 1
fi
done
36 changes: 36 additions & 0 deletions .claude/hooks/session-start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/bash
# SessionStart hook for Claude Code cloud sessions: installs the .NET SDK this
# repository's global.json asks for and restores its packages, so a session
# can build and test straight away.
set -euo pipefail

if [ "${CLAUDE_CODE_REMOTE:-}" != "true" ]; then

Check failure on line 7 in .claude/hooks/session-start.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsXmyYDpVM_D-meC&open=AaFZSsXmyYDpVM_D-meC&pullRequest=145
exit 0
fi

cd "$CLAUDE_PROJECT_DIR"

minimum=10.0.100
if [ -f global.json ]; then

Check failure on line 14 in .claude/hooks/session-start.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsXmyYDpVM_D-meD&open=AaFZSsXmyYDpVM_D-meD&pullRequest=145
minimum="$(jq -r '.sdk.version // "10.0.100"' global.json)"
fi
# The test projects multi-target, so the older runtimes are needed to run them.
bash .claude/hooks/install-dotnet.sh "${minimum%.*}" "$minimum" 8.0 9.0

export DOTNET_ROOT="$HOME/.dotnet"
export PATH="$DOTNET_ROOT:$DOTNET_ROOT/tools:$PATH"
export DOTNET_CLI_TELEMETRY_OPTOUT=1
export DOTNET_NOLOGO=1
if [ -n "${CLAUDE_ENV_FILE:-}" ]; then

Check failure on line 24 in .claude/hooks/session-start.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsXmyYDpVM_D-meE&open=AaFZSsXmyYDpVM_D-meE&pullRequest=145
{
echo "export DOTNET_ROOT=\"$DOTNET_ROOT\""
echo "export PATH=\"$DOTNET_ROOT:$DOTNET_ROOT/tools:\$PATH\""
echo 'export DOTNET_CLI_TELEMETRY_OPTOUT=1'
echo 'export DOTNET_NOLOGO=1'
} >>"$CLAUDE_ENV_FILE"
fi

if [ -f .config/dotnet-tools.json ]; then

Check failure on line 33 in .claude/hooks/session-start.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[[' instead of '[' for conditional tests. The '[[' construct is safer and more feature-rich.

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_CodeBlocker&issues=AaFZSsXmyYDpVM_D-meF&open=AaFZSsXmyYDpVM_D-meF&pullRequest=145
dotnet tool restore
fi
dotnet restore
14 changes: 14 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh"
}
]
}
]
}
}
Loading