# yaml-language-server: $schema=https://json.schemastore.org/github-action.json # permissions: # - contents: read # Required for checking out repository --- name: C# Build description: 'Builds and tests C# projects.' author: 'Ismo Vuorinen' branding: icon: 'code' color: 'blue' inputs: dotnet-version: description: 'Version of .NET SDK to use.' required: false max-retries: description: 'Maximum number of retry attempts for dotnet restore operations' required: false default: '3' token: description: 'GitHub token for authentication' required: false default: '' outputs: build_status: description: 'Build completion status (success/failure)' value: ${{ steps.build.outputs.status }} test_status: description: 'Test execution status (success/failure/skipped)' value: ${{ steps.test.outputs.status }} dotnet_version: description: 'Version of .NET SDK used' value: ${{ steps.detect-dotnet-version.outputs.detected-version }} artifacts_path: description: 'Path to build artifacts' value: '**/bin/Release/**/*' test_results_path: description: 'Path to test results' value: '**/*.trx' runs: using: composite steps: - name: Checkout Repository uses: actions/checkout@71cf2267d89c5cb81562390fa70a37fa40b1305e # v6-beta with: token: ${{ inputs.token || github.token }} - name: Detect .NET SDK Version id: detect-dotnet-version shell: sh env: DEFAULT_VERSION: "${{ inputs.dotnet-version || '7.0' }}" run: | set -eu # Function to validate version format validate_version() { version=$1 case "$version" in [0-9]* | [0-9]*\.[0-9]* | [0-9]*\.[0-9]*\.[0-9]*) return 0 ;; *) return 1 ;; esac } # Function to clean version string clean_version() { printf '%s' "$1" | sed 's/^[vV]//' | tr -d ' \n\r' } detected_version="" # Parse .tool-versions file if [ -f .tool-versions ]; then echo "Checking .tool-versions for dotnet..." >&2 version=$(awk '/^dotnet[[:space:]]/ {gsub(/#.*/, ""); print $2; exit}' .tool-versions 2>/dev/null || echo "") if [ -n "$version" ]; then version=$(clean_version "$version") if validate_version "$version"; then echo "Found .NET version in .tool-versions: $version" >&2 detected_version="$version" fi fi fi # Parse Dockerfile if [ -z "$detected_version" ] && [ -f Dockerfile ]; then echo "Checking Dockerfile for dotnet..." >&2 version=$(grep -iF "FROM" Dockerfile | grep -F "dotnet:" | head -1 | \ sed -n -E "s/.*dotnet:([0-9]+(\.[0-9]+)*)(-[^:]*)?.*/\1/p" || echo "") if [ -n "$version" ]; then version=$(clean_version "$version") if validate_version "$version"; then echo "Found .NET version in Dockerfile: $version" >&2 detected_version="$version" fi fi fi # Parse devcontainer.json if [ -z "$detected_version" ] && [ -f .devcontainer/devcontainer.json ]; then echo "Checking devcontainer.json for dotnet..." >&2 if command -v jq >/dev/null 2>&1; then version=$(jq -r '.image // empty' .devcontainer/devcontainer.json 2>/dev/null | sed -n -E "s/.*dotnet:([0-9]+(\.[0-9]+)*)(-[^:]*)?.*/\1/p" || echo "") if [ -n "$version" ]; then version=$(clean_version "$version") if validate_version "$version"; then echo "Found .NET version in devcontainer: $version" >&2 detected_version="$version" fi fi else echo "jq not found; skipping devcontainer.json parsing" >&2 fi fi # Parse global.json if [ -z "$detected_version" ] && [ -f global.json ]; then echo "Checking global.json..." >&2 if command -v jq >/dev/null 2>&1; then version=$(jq -r '.sdk.version // empty' global.json 2>/dev/null || echo "") if [ -n "$version" ]; then version=$(clean_version "$version") if validate_version "$version"; then echo "Found .NET version in global.json: $version" >&2 detected_version="$version" fi fi else echo "jq not found; skipping global.json parsing" >&2 fi fi # Use default version if nothing detected if [ -z "$detected_version" ]; then detected_version="$DEFAULT_VERSION" echo "Using default .NET version: $detected_version" >&2 fi # Set output printf 'detected-version=%s\n' "$detected_version" >> "$GITHUB_OUTPUT" echo "Final detected .NET version: $detected_version" >&2 - name: Setup .NET SDK uses: actions/setup-dotnet@2016bd2012dba4e32de620c46fe006a3ac9f0602 # v5.0.1 with: dotnet-version: ${{ steps.detect-dotnet-version.outputs.detected-version }} cache: true cache-dependency-path: '**/packages.lock.json' - name: Restore Dependencies uses: step-security/retry@e1d59ce1f574b32f0915e3a8df055cfe9f99be5d # v3.0.4 with: timeout_minutes: 10 max_attempts: ${{ inputs.max-retries }} command: | echo "Restoring .NET dependencies..." dotnet restore --verbosity normal - name: Build Solution id: build shell: sh run: | set -eu echo "Building .NET solution..." if dotnet build --configuration Release --no-restore --verbosity normal; then echo "status=success" >> "$GITHUB_OUTPUT" echo "Build completed successfully" else echo "status=failure" >> "$GITHUB_OUTPUT" echo "Build failed" exit 1 fi - name: Run Tests id: test shell: sh run: | set -eu echo "Running .NET tests..." if find . -name "*.csproj" | xargs grep -lE "(Microsoft\.NET\.Test\.Sdk|xunit|nunit)" | head -1 | grep -q .; then if dotnet test --configuration Release --no-build \ --collect:"XPlat Code Coverage" \ --logger "trx;LogFileName=test-results.trx" \ --verbosity normal; then echo "status=success" >> "$GITHUB_OUTPUT" echo "Tests completed successfully" else echo "status=failure" >> "$GITHUB_OUTPUT" echo "Tests failed" exit 1 fi else echo "No test projects found, skipping test execution." echo "status=skipped" >> "$GITHUB_OUTPUT" fi - name: Upload Test Results if: always() uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: csharp-test-results path: | **/*.trx **/TestResults/**/coverage.cobertura.xml if-no-files-found: ignore