ConfigureBillingService() walks AppDomain.CurrentDomain.GetAssemblies()
and calls Assembly.GetTypes() on each. If any of the loaded assemblies
has a type that fails to resolve (a flaky dependency, an AddOn with a
broken reference, a test dependency that's been rewritten after compile),
GetTypes() throws ReflectionTypeLoadException (or, less commonly,
FileNotFoundException / TypeLoadException for the assembly itself).
In CI on the forgejo-runner (and especially in test discovery under
xunit v3), one such assembly is loaded somewhere between test runs and
silently throws. The exception is not handled, so:
1. Collections are Cleared at the top of ConfigureBillingService().
2. The reflection loop throws before reaching the
RegisterBilling<HairCutQuery/HairMultiCutQuery/RdvQuery> calls.
3. BillingService.Billing ends up empty (Count = 0).
4. The second ConfigureBillingService() call sees the same assembly
loaded (xunit v3 keeps the AppDomain warm for the whole suite),
throws identically, and the test
Yavsc.BillingServiceTests.ConfigureBillingService_CanBeCalledTwiceWithoutThrowing
fails with 'Assert.Equal() Failure: Expected 3, Actual 0'.
Fix: catch ReflectionTypeLoadException and use the partial
.Types() list (the successfully-resolved subset), and use a
broader catch (with continue) for any other assembly-level
load failure. The lost user-settings types are not material;
they are derived from ApplicationDbContext in a separate loop
right after, and the RegisterBilling<>() calls that populate
BillingService.Billing run last, after both reflective phases
have completed best-effort.
The test still passes locally because the local test environment
loads a clean set of assemblies; only the CI runner (with its
extra test-time tooling) hits this path.
Patch is even (6) and bare, so this is classified as 'stable' by
the validate-release job in .github/workflows/docker-publish-android.yml.
Move the Unreleased section up by inserting [1.0.6] below it, with
a list of changes that landed on this release:
- Self-hosted Forgejo Actions runner now drives CI on yavsc,
using pazof/yavsc-build-env:debian12-dotnet10-android36-v1
pulled from Docker Hub.
- .forgejo/workflows/buildAndTest.yml builds without
actions/checkout (image has no Node) and uses NuGet.config
for the isn.pschneider.fr feed.
- Dockerfile / Dockerfile.backend drop the redundant
'dotnet nuget add source' step that broke the APK build on
GitHub Actions.
The project-level NuGet.config (added in 94012c51) lists the isn feed
so 'dotnet restore' picks it up without an inline 'dotnet nuget add
source' step.
The inline add source was duplicating NuGet.config and causing build
failures in GitHub Actions:
- The --allow-insecure-connections flag did not match the actual
HTTPS deployment of isn.pschneider.fr (Letsencrypt-issued cert,
not self-signed), making the step fail with 'exit code 1'.
- docker build --target build-env (used by
.github/workflows/docker-publish-android.yml) hit this on every
run.
Both Dockerfile and Dockerfile.backend had the same redundant step;
both removed. 'dotnet restore' still finds the feed via NuGet.config
at /src/NuGet.config (copied in by 'COPY . .').