name: .NET Build & Test (Reusable) on: workflow_call: inputs: solution: description: "Solution/project file to build (relative to working-directory). Empty = let dotnet pick the single sln/proj in working-directory" required: false default: "" type: string dotnet-version: description: ".NET SDK version" required: false default: "10.0.x" type: string configuration: description: "Build configuration" required: false default: "Release" type: string # string, not boolean: act_runner mangles boolean defaults. The value may still arrive as '%!t(string=…)', see Build step. warnaserror: description: "Treat build warnings as errors (\"true\"/\"false\")" required: false default: "true" type: string working-directory: description: "Path where dotnet commands are executed (relative to repo root)" required: false default: "." type: string test-filter: description: "Optional dotnet test --filter expression" required: false default: "" type: string jobs: build-and-test: runs-on: ubuntu-latest env: DOTNET_CLI_TELEMETRY_OPTOUT: "1" DOTNET_NOLOGO: "1" SOLUTION: ${{ inputs.solution }} CONFIGURATION: ${{ inputs.configuration }} WARNASERROR: ${{ inputs.warnaserror }} TEST_FILTER: ${{ inputs.test-filter }} defaults: run: shell: bash working-directory: ${{ inputs.working-directory }} steps: - name: Checkout uses: actions/checkout@v6 - name: Setup .NET uses: actions/setup-dotnet@v6 with: dotnet-version: ${{ inputs.dotnet-version }} - name: Restore run: dotnet restore ${SOLUTION:+"$SOLUTION"} - name: Build run: | ARGS=(-c "$CONFIGURATION" --no-restore) # act_runner may deliver the value mangled as '%!t(string=false)', so match by substring. case "${WARNASERROR,,}" in *false*) ;; *) ARGS+=(-warnaserror) ;; esac set -x dotnet build ${SOLUTION:+"$SOLUTION"} "${ARGS[@]}" - name: Test run: | ARGS=(-c "$CONFIGURATION" --no-build) if [ -n "$TEST_FILTER" ]; then ARGS+=(--filter "$TEST_FILTER") fi dotnet test ${SOLUTION:+"$SOLUTION"} "${ARGS[@]}"