Skip to content
Merged
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
196 changes: 196 additions & 0 deletions .github/workflows/build-samples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ on:
- "csharp-language/modern-patterns-result-pipeline/**"
- "dotnet-8-essentials/background-jobs-hostedservice-queues/**"
- "dotnet-8-essentials/configuration-secrets-environments/**"
- "dotnet-8-essentials/core-features-get-started/**"
- ".github/workflows/build-samples.yml"

pull_request:
Expand All @@ -50,6 +51,7 @@ on:
- "csharp-language/modern-patterns-result-pipeline/**"
- "dotnet-8-essentials/background-jobs-hostedservice-queues/**"
- "dotnet-8-essentials/configuration-secrets-environments/**"
- "dotnet-8-essentials/core-features-get-started/**"
- ".github/workflows/build-samples.yml"

workflow_dispatch:
Expand Down Expand Up @@ -1232,3 +1234,197 @@ jobs:
echo "Secret-like CI value leaked into an HTTP response."
exit 1
fi

test-native-aot-essentials:
name: Test Native AOT essentials sample
runs-on: ubuntu-latest

permissions:
contents: read

steps:
- name: Check out repository
uses: actions/checkout@v5

- name: Install .NET 10 SDK
uses: actions/setup-dotnet@v5
with:
dotnet-version: "10.0.x"

- name: Install Native AOT prerequisites
shell: bash
run: |
sudo apt-get update

sudo apt-get install \
--yes \
clang \
zlib1g-dev

- name: Restore
run: >
dotnet restore
dotnet-8-essentials/core-features-get-started/NativeAotEssentialsMinimal.slnx

- name: Build
run: >
dotnet build
dotnet-8-essentials/core-features-get-started/NativeAotEssentialsMinimal.slnx
--configuration Release
--no-restore

- name: Test
run: >
dotnet test
dotnet-8-essentials/core-features-get-started/NativeAotEssentialsMinimal.slnx
--configuration Release
--no-build

- name: Verify application has no direct packages
shell: bash
run: |
output="$(
dotnet list \
dotnet-8-essentials/core-features-get-started/src/NativeAotEssentialsMinimal/NativeAotEssentialsMinimal.csproj \
package
)"

printf '%s\n' "${output}"

if grep \
--extended-regexp \
--quiet \
'<PackageReference\s' \
dotnet-8-essentials/core-features-get-started/src/NativeAotEssentialsMinimal/NativeAotEssentialsMinimal.csproj; then
echo "Application project declares a direct package reference."
exit 1
fi

- name: Publish Native AOT
shell: bash
run: |
publish_dir="${RUNNER_TEMP}/native-aot-essentials"
publish_log="${RUNNER_TEMP}/native-aot-publish.log"

rm -rf "${publish_dir}"

set -o pipefail

dotnet publish \
dotnet-8-essentials/core-features-get-started/src/NativeAotEssentialsMinimal/NativeAotEssentialsMinimal.csproj \
--configuration Release \
--runtime linux-x64 \
--output "${publish_dir}" \
2>&1 |
tee "${publish_log}"

test -x \
"${publish_dir}/NativeAotEssentialsMinimal"

if grep \
--extended-regexp \
--ignore-case \
'warning (IL[0-9]+|CS[0-9]+|NETSDK[0-9]+)' \
"${publish_log}"; then
echo "Native AOT publish produced a compiler, SDK, trim, or AOT warning."
exit 1
fi

- name: Smoke-test native executable
shell: bash
run: |
publish_dir="${RUNNER_TEMP}/native-aot-essentials"
app_log="${RUNNER_TEMP}/native-aot-essentials.log"

ASPNETCORE_URLS="http://127.0.0.1:5098" \
DOTNET_ENVIRONMENT="Production" \
"${publish_dir}/NativeAotEssentialsMinimal" \
>"${app_log}" 2>&1 &

app_pid="$!"

cleanup() {
kill "${app_pid}" 2>/dev/null || true
wait "${app_pid}" 2>/dev/null || true
}

trap cleanup EXIT

for attempt in $(seq 1 60); do
if curl \
--fail \
--silent \
http://127.0.0.1:5098/ \
>"${RUNNER_TEMP}/native-aot-root.json"; then
break
fi

if ! kill -0 "${app_pid}" 2>/dev/null; then
cat "${app_log}"
exit 1
fi

sleep 0.25
done

root="$(
cat "${RUNNER_TEMP}/native-aot-root.json"
)"

expected_root='{"sample":"native-aot-essentials","builder":"CreateSlimBuilder","json":"source-generated"}'

if [[ "${root}" != "${expected_root}" ]]; then
printf 'Unexpected root response:\n%s\n' "${root}"
exit 1
fi

runtime="$(
curl \
--fail \
--silent \
http://127.0.0.1:5098/runtime
)"

printf '%s\n' "${runtime}"

printf '%s\n' "${runtime}" |
grep \
--fixed-strings \
--quiet \
'"dynamicCodeSupported":false'

printf '%s\n' "${runtime}" |
grep \
--fixed-strings \
--quiet \
'"dynamicCodeCompiled":false'

echo_response="$(
curl \
--fail \
--silent \
--request POST \
--header 'Content-Type: application/json' \
--data '{"message":" hello native aot "}' \
http://127.0.0.1:5098/echo
)"

expected_echo='{"message":"HELLO NATIVE AOT","length":16}'

if [[ "${echo_response}" != "${expected_echo}" ]]; then
printf 'Unexpected echo response:\n%s\n' "${echo_response}"
exit 1
fi

invalid_status="$(
curl \
--silent \
--output "${RUNNER_TEMP}/native-aot-invalid.json" \
--write-out '%{http_code}' \
--request POST \
--header 'Content-Type: application/json' \
--data '{"message":" "}' \
http://127.0.0.1:5098/echo
)"

test "${invalid_status}" = "400"
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Each sample folder contains a focused implementation of one tutorial topic. The
| [`csharp-language/modern-patterns-result-pipeline`](csharp-language/modern-patterns-result-pipeline/) | Focused .NET 10 console companion locked to C# 12.0, demonstrating a custom Result type, safe and diagnostic errors, Map/Bind/BindAsync composition, invariant text-import validation, short-circuit persistence, cancellation, deterministic output, and tests | [C# 12 Functional Patterns: Result Type, Error Handling & Composable Pipeline Testing](https://www.dotnet-guide.com/tutorials/csharp-language/modern-patterns-result-pipeline/) |
| [`dotnet-8-essentials/background-jobs-hostedservice-queues`](dotnet-8-essentials/background-jobs-hostedservice-queues/) | Focused ASP.NET Core Minimal API demonstrating a bounded Channel queue, asynchronous backpressure, a BackgroundService consumer, fresh scoped handlers, safe in-memory job status, exception isolation, cancellation, and integration tests | [.NET 8 Background Jobs: IBackgroundTaskQueue, BackgroundService & Production-Ready Patterns](https://www.dotnet-guide.com/tutorials/dotnet-8-essentials/background-jobs-hostedservice-queues/) |
| [`dotnet-8-essentials/configuration-secrets-environments`](dotnet-8-essentials/configuration-secrets-environments/) | Focused .NET 10 Minimal API demonstrating layered configuration precedence, prefixed environment variables, command-line overrides, strongly typed options, startup validation, User Secrets metadata, a lightweight feature flag, and safe Development-only diagnostics | [.NET 8 Configuration & Secrets Management: Typed Options, User Secrets & Feature Flags](https://www.dotnet-guide.com/tutorials/dotnet-8-essentials/configuration-secrets-environments/) |
| [`dotnet-8-essentials/core-features-get-started`](dotnet-8-essentials/core-features-get-started/) | Focused .NET 10 Native AOT Minimal API demonstrating CreateSlimBuilder, source-generated JSON, typed DI, AOT-safe endpoints, analyzer-aware publishing, and direct native-binary smoke testing | [.NET 8 Essentials: Core Features & Getting Started](https://www.dotnet-guide.com/tutorials/dotnet-8-essentials/core-features-get-started/) |

## Companion articles
- [Common Microsoft.Extensions.AI mistakes](https://www.dotnet-guide.com/articles/dotnet-ai/microsoft-extensions-ai-common-mistakes/)
Expand Down Expand Up @@ -175,6 +176,24 @@ tutorials/
| `-- ConfigPrecedenceMinimal.Tests/
| |-- ConfigPrecedenceMinimal.Tests.csproj
| `-- ConfigurationTests.cs
| `-- core-features-get-started/
| |-- NativeAotEssentialsMinimal.slnx
| |-- README.md
| |-- src/
| | `-- NativeAotEssentialsMinimal/
| | |-- NativeAotEssentialsMinimal.csproj
| | |-- Program.cs
| | |-- Models/
| | | `-- ApiModels.cs
| | |-- Serialization/
| | | `-- AppJsonSerializerContext.cs
| | `-- Services/
| | |-- ITextTransformer.cs
| | `-- TextTransformer.cs
| `-- tests/
| `-- NativeAotEssentialsMinimal.Tests/
| |-- NativeAotEssentialsMinimal.Tests.csproj
| `-- NativeAotEssentialsTests.cs
|-- blazor/
| |-- create-interactive-ui-csharp-12/
| | |-- BlazorTodoMinimal.slnx
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Solution>
<Folder Name="/src/">
<Project Path="src/NativeAotEssentialsMinimal/NativeAotEssentialsMinimal.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/NativeAotEssentialsMinimal.Tests/NativeAotEssentialsMinimal.Tests.csproj" />
</Folder>
</Solution>
Loading
Loading