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
18 changes: 12 additions & 6 deletions .github/workflows/canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,24 @@ jobs:
# so both processes select the highest installed major rather than staying on a stable one;
# * DOTNET_ROLL_FORWARD_TO_PRERELEASE=1 lets a stable net8 app bind a *prerelease* runtime, which
# roll-forward refuses by default.
# A guard first confirms a newer major than the build SDK is actually installed, so the canary never
# false-passes by silently running on .NET 10. A regression in the preview runtime's roll-forward behaviour
# then turns this step red, and the scheduled run notifies the maintainer.
# The step PRINTS the installed runtimes and the highest major first, so the log shows the preview it is about
# to exercise instead of leaving it to be inferred. It then guards: only a runtime newer than the .NET 10 build
# SDK proves anything, so if the preview install did not yield one the run skips neutrally rather than
# false-passing on .NET 10. fce runs with --verbose, so the worker additionally logs the exact runtime it bound
# to (its "Documenting '…' on .NET <version>." banner).
- name: Generate documentation on the preview runtime
if: steps.preview.outcome == 'success'
env:
DOTNET_ROLL_FORWARD: LatestMajor
DOTNET_ROLL_FORWARD_TO_PRERELEASE: '1'
run: |
set -euo pipefail
if ! dotnet --list-runtimes | grep -Eq 'Microsoft\.NETCore\.App (1[1-9]|[2-9][0-9])\.'; then
echo "::notice::the installed 'preview' is not a newer major than the build SDK; skipping this run"
echo "Installed .NET runtimes:"
dotnet --list-runtimes
newest="$(dotnet --list-runtimes | sed -nE 's/^Microsoft\.NETCore\.App ([0-9]+)\..*/\1/p' | sort -n | tail -1)"
echo "Highest installed .NET major: ${newest:-none}"
if [ "${newest:-0}" -le 10 ]; then
echo "::notice::no runtime newer than the .NET 10 build SDK is installed; skipping this run"
exit 0
fi
dotnet FirstClassErrors.Cli/bin/Release/net8.0/fce.dll generate \
Expand All @@ -99,4 +105,4 @@ jobs:
cat canary-catalog.json
exit 1
fi
echo "ok: the net8 fce and worker documented a net8 target on the .NET preview runtime"
echo "ok: the net8 fce and worker documented a net8 target on the .NET ${newest} preview runtime"
17 changes: 16 additions & 1 deletion FirstClassErrors.GenDoc.Worker/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text.Json;
using System.Text.Json.Serialization;

Expand All @@ -17,7 +18,10 @@
// The worker is meant to be launched by the generator against the target's own dependency closure
// (dotnet exec --depsfile <target>.deps.json ... worker.dll <target>.dll), so it binds to the target's
// FirstClassErrors version and starts from a fresh static registry. It writes the ErrorDocumentationExtractionResult
// as JSON to the output file when provided, otherwise to stdout; diagnostics and fatal errors go to stderr.
// as JSON to the output file when provided, otherwise to stdout. Fatal errors go to stderr. It also logs a one-line
// runtime banner (the framework it actually bound to): on stdout when the result goes to a file — stdout then carries
// no data and the host surfaces the banner as an 'info' diagnostic — and on stderr in the stdout-JSON mode so the
// result stays pure.
//
// When --culture is given, the extraction runs under that culture, so documentation factories that read localized
// resources produce their text in that language.
Expand Down Expand Up @@ -53,6 +57,17 @@
}

try {
// Report the runtime this worker actually bound to. Its runtimeconfig rolls forward independently of the
// launching tool (RollForward=LatestMajor), so this can differ from the CLI's runtime and is the authoritative
// record of where the target was loaded — which is what the preview canary reads back. See the header note for
// the stdout/stderr split.
string runtimeBanner = $"Documenting '{assemblyPath}' on {RuntimeInformation.FrameworkDescription}.";
if (outputPath is null) {
await Console.Error.WriteLineAsync(runtimeBanner);
} else {
await Console.Out.WriteLineAsync(runtimeBanner);
}

Assembly assembly = LoadTarget(assemblyPath);
ErrorDocumentationExtractionResult result = AssemblyErrorDocumentationReader.GetErrorDocumentationFrom(assembly);

Expand Down