Conversation
build.fsx is about to be written against FAKE 6 instead of the FakeLib.dll of the Build group. Those modules target net6.0/netstandard2.0, so they cannot live in the Build group, which is pinned to `framework >= net461` for FSharp.Formatting and the .NET Framework reference assemblies. Add a BuildScript group restricted to net10.0, the framework `dotnet fsi` runs on, and have the two entry points generate its load scripts after the restore.
ILRepack.exe is a .NET Framework binary, so on Linux FAKE used to run it through Mono. The dotnet-ilrepack tool ships the same repacker as a .NET tool and needs no Mono. Mono also supplied the .NET Framework reference assemblies implicitly; under dotnet they have to be passed explicitly, so point the repacker at the same directory MSBuild already resolves net461 against.
FakeLib.dll is FAKE 4.64.17, a .NETFramework 4.5 assembly from 2017 that only happens to load under .NET 10. Rewrite the script against the FAKE 6 modules of the BuildScript group instead. Two behaviour-preserving details worth calling out: - FAKE 4 derived `isMono` from Environment.OSVersion.Platform, so it was true on Linux and the `not isMono` guards really meant "on Windows". FAKE 6 tests for the Mono runtime, which is false under .NET 10 everywhere, so those guards become Environment.isWindows rather than a literal translation. - DotNet.test defaults to the Debug configuration, unlike build, publish and pack. With --no-build that would look for bin/Debug, so the configuration is now explicit on every test invocation. The docs targets keep shelling out to the FAKE 4 runner: docs/tools/generate.fsx is still bound to FSharp.Formatting 3 and its Razor templates.
The load scripts build.fsx reads are generated, so running `dotnet fsi build.fsx` on its own needs build.sh or build.cmd to have run first. Mono is now only needed for the net461 test passes.
…Test Paket.Tests.fsproj sets Authors and Description for the assembly metadata tests, but not Version: `Loading assembly metadata works` compares the version baked into Paket.Tests.dll with the one in RELEASE_NOTES.md, and that version is injected by the build script. The Build target passes it, so RunTests is green against the assembly it produced. QuickTest builds through dotnet test instead, without the property, and since it depends on Clean the rebuild always happens: the assembly ended up at the default 1.0.0 and the test failed. CI only runs BuildPackage, so this stayed invisible.
FAKE 6's DotNet.install defaults to ~/.dotnet and can also settle on a dotnet found through PATH, while FAKE 4 installed into LocalApplicationData/dotnetcore. The CI installs the SDK and the .NET 8 runtime the integration tests need into that directory, so pin it through CustomInstallDir.
Array.tryFind kept the first bare argument and silently dropped the rest, while the FAKE runner used to let the last one win. Fail with an explicit message instead of quietly running a different target.
MergePaketTool no longer runs ILRepack.exe under Mono, the target name is no longer passed as an environment variable, and build.sh does not run on Mono.
…and-rolled helper
…ectory.Build.props
The FAKE 4 call passed TimeSpan.FromMinutes 5. to ExecProcess, so a hung ILRepack was killed and reported as a build failure. DotNet.exec defaults to Options.Timeout = None, which let a stuck merge run until the CI job timeout instead.
…ath can't be derived Seq.exactlyOne threw a bare ArgumentException as soon as Directory.Build.props gained a second, conditioned TargetFrameworkRootPath, and a value written with another MSBuild property left the literal $(...) in the ILRepack /lib argument, surfacing as an unresolvable mscorlib. Name the props file in both cases.
The path was built with backslashes, so on Linux it named a file that does not exist. Nuspec.Load turns a missing file into Nuspec.All without a word, so the test compared its expectations against an empty nuspec: no frameworkAssemblies (System.Data, System.Xml) and no <references> filter, hence all three libraries in the generated Xml. This stayed invisible because the test never ran anywhere: [<Flaky>] is only applied under TESTSUITE_RUNS_ON_DOTNETCORE, and the netcore unit-test pass was asking for a framework the project does not target.
|
Previous build on fake 4 crash on GenerateHelp, and not execute:
|
System.Net.Http.WinHttpHandler references System.Buffers 4.0.3.0, but net461 resolves to the netstandard2.0 asset of System.Buffers 4.6.1, stamped 4.0.2.0. MSBuild only unifies references upwards, so AutoGenerateBindingRedirects warns with MSB3277 and emits no redirect at all, and every HTTP response dies in WinHttpResponseParser with a FileLoadException. Only bin/net461/paket.exe is affected -- the one the .NET Framework integration tests drive. The shipped bin/merged/paket.exe repacks System.Buffers and WinHttpHandler into a single assembly, so it never resolves the reference.
Mirrors the < /dev/null already used by the Linux job. The Windows job hung for 59 minutes after 24 integration tests without emitting a line, and a process waiting on stdin is the cheapest hypothesis to rule out. Requires shell: cmd, since < NUL is not PowerShell syntax.
…t assemblies The spawned bin/net461/paket.exe was only half the problem: tests that drive Paket.Core in-process run under the test host, which binds against Paket.IntegrationTests.dll.config, not paket.exe.config. #1298 still died in WinHttpResponseParser through Paket.Dependencies.FindPackageVersions. Auditing every assembly reference in both net461 test outputs against the versions actually shipped there turns up System.Buffers as the only reference not covered by a redirect, in both projects.
Only failures were printed, so the run that hangs past the 60-minute timeout leaves no record of which test never returned -- and the timeout kills it before the trx is written. RunTests already reports at this verbosity.
The suite runs in a few minutes when healthy, so 30 minutes is still a wide margin, and a run that hangs reports back twice as fast.
…est suite ExecProcessWithLambdas (FakeLib 4.64.17) ends with an unbounded WaitForExit() that the 7-minute timeout above it does not cover: IL_020b: WaitForExit(int32) // the timeout IL_0217: Kill() // "Process %s %s timed out." IL_02b0: WaitForExit() // unbounded, after IL_02b6: get_ExitCode() That call returns only once the redirected streams reach EOF. With node reuse on, dotnet build/run/pack leave MSBuild worker nodes behind holding the inherited stdout and stderr handles, so EOF never comes and the run hangs with no output -- which is why PROCESS TIMED OUT never appeared in the log. ConditionSpecs is the only fixture that nests dotnet run and dotnet pack, and it is where the Windows run stops.
The scenario is named i001195-broken-appconfig and the test asserts paket reports a parse error naming Project1 and app.config. The brokenness was a '<<<<<<< HEAD' conflict marker left in the file on purpose; dd0b881 stripped it as if it were a stray merge residue, which turned the fixture into valid XML. paket then had nothing to report and the test failed with 'paket should fail'.
The suite reached its end for the first time: 226 tests, 212 passed, 12 failed. None of the failures comes from the FAKE 6 port -- they are the debt of a suite that had never run in CI on any platform. Each is marked with what it actually does, so the 212 that do pass are protected from here on. Notably 'apply framework restriction' is a real resolution bug, not a stale bound: it resolves Microsoft.AspNetCore.WebUtilities 10.0.11 under 'framework: =net8.0' even though that version only ships lib/net10.0. Raising the assertion would have disarmed the test. Tracked by #4346.
Both passed in the previous Windows run and failed in the next, with the endpoints they depend on healthy throughout -- they fail on third-party availability, not on anything in this repository. #3014 calls api.github.com, which allows unauthenticated callers 60 requests an hour per IP, shared across hosted runners; Paket takes its GitHub credentials from paket.dependencies, so there is no token to hand it from CI. #1635 asserts on a specific 401 from myget.org, so any hiccup produces a different message. Flaky is the category this repository already keeps for exactly this, and it moves them off the main job.
Update — the Windows job now runs the integration suite, for the first timeGreen on The Windows job used to pass by accidentOn That settles the Two real bugs it uncoveredMissing The suite could hang forever ( Separately, What I parked, and why (
|
| test(s) | marked | reason |
|---|---|---|
apply framework restriction (×4) |
Ignore |
framework restriction not applied to the resolution |
#2694, #3558, #3983 |
Ignore |
the expected .nupkg is never produced |
#3317 |
Ignore |
dotnet pack exits 1 |
#4183 |
Ignore |
Many groups detected map [], floating deps dropped |
#2684 |
Ignore |
second restore still reports mismatching hashes |
run fsi …deterministic output |
Ignore |
2 of 8 scripts no longer produce their expected output |
#3014, #1635 |
Flaky |
network-dependent, see below |
One deserves a closer look before merge: apply framework restriction is not a stale bound. It resolves Microsoft.AspNetCore.WebUtilities 10.0.11 under framework: =net8.0, although that version ships only lib/net10.0 and 8.0.21 is the newest one that fits. Raising the assertion to < 11 would have disarmed a test that is catching a genuine resolution bug, so it is ignored with that diagnosis written above it instead.
#3014 and #1635 are network flakes, not failures: they hit api.github.com (60 unauthenticated requests an hour per IP, shared across hosted runners) and myget.org, they passed in one Windows run and failed in the next with both endpoints healthy, and they pass locally. [<Flaky>] is the category this repo already keeps for that — with the consequence that the Linux flaky job now inherits them.
Result
Windows on c6bb869e9: Status: Ok. net461 integration 224 discovered / 211 passed / 13 skipped in 16 minutes; net10.0 227 / 207 / 20. Unit tests unchanged.
And the green now means something: with the pwsh default the job reported success over Status: Failure, whereas with shell: cmd (0a26cdf3d, added to close stdin) the failing run correctly reported Process completed with exit code 1. Supporting changes: normal verbosity on the integration run so that a hang names the test that never returned (a721b1346), and the timeout cut from 60 to 30 minutes now that the suite takes ~16 (be223af75).
Still open for you to call
- The
Octokit.fsxre-resolution noted in the description is unchanged — leave it, revert it, or pin it the wayenricosada/add_icon_to_exeis. - Skipping the doc targets on CI is currently a side effect of
isLocalBuildrather than a deliberate guard. Given thatGenerateHelpfails on the Windows runner anyway (Build broken since myget roslyn-tools is gone #4002), skipping looks right, but say the word if you would rather it were explicit.
Part of #4348 — the "upgrade to fake 6" step of the plan, after #4422 removed the FAKE runner from the entry points.
What changes
FAKE 6 as a library.
build.fsxwas written againstFakeLib.dll, i.e. FAKE 4.64.17 — a .NETFramework 4.5 assembly from 2017 that only happens to load under .NET 10. It is now written against the FAKE 6 modules. Those target net6.0/netstandard2.0, so they cannot live in theBuildgroup, which is pinned toframework >= net461for FSharp.Formatting and the reference assemblies; they get a newBuildScriptgroup restricted to net10.0, the frameworkdotnet fsiruns on.build.sh/build.cmdgenerate its load scripts after the restore, so runningdotnet fsi build.fsx <Target>directly assumes one of the entry points has run at least once.dotnet-ilrepackinstead ofILRepack.exe.ILRepack.exeis a .NET Framework binary, so on Linux FAKE ran it through Mono. Thedotnet-ilrepacktool ships the same repacker as a .NET tool. Mono also supplied the .NET Framework reference assemblies implicitly; underdotnetthey have to be passed explicitly, so the repacker is pointed at the same directory MSBuild already resolves net461 against (TargetFrameworkRootPathfromDirectory.Build.props).MergePaketTooltherefore no longer needs Mono. It is still needed for thenet461test passes and forPublishNuGet, which pushes with the mergednet461paket.exe.Behaviour differences worth reviewing
isMonoguards becameEnvironment.isWindows. FAKE 4 derivedisMonofromEnvironment.OSVersion.Platform, so it was true on Linux andnot isMonoreally meant "on Windows". FAKE 6 tests for the Mono runtime, which is false under .NET 10 everywhere, so a literal translation would have silently enabled those targets on Linux.DotNet.testdefaults to Debug, unlike build/publish/pack. With--no-buildthat would look forbin/Debug, so the configuration is now explicit on every test invocation.BuildServer.isLocalBuildchanges meaning on CI. FAKE 4 reportedisLocalBuild = trueeven on GitHub Actions; FAKE 6 detects the build server and reportsfalse. The doc targets are guarded by it, soGenerateDocs/GenerateReferenceDocs/GenerateHelpnow no longer run on the Windows runner. That looks desirable —docs/tools/generate.fsxis still bound to FSharp.Formatting 3 and its Razor templates, and the docs targets still shell out to the FAKE 4 runner for it — but it is a side effect of the library swap rather than a deliberate decision, so say the word if it should be made explicit instead.github fsharp/FAKE modules/Octokit/Octokit.fsxwas re-resolved inpaket.lock(13eee5a7…→e8e1cae7…) as a side effect of the restore; it has no SHA inpaket.dependencies.build.fsx#loads it unconditionally, so it affects every target, not justReleaseGitHub. The new revision still exposes thecreateClient/createDraft/uploadFileAPI the script uses, so nothing is broken — but it can be reverted or pinned the wayenricosada/add_icon_to_exealready is, if preferred. Replace vendored source files and legacy build dependencies #4350 proposes replacing it outright.The SDK install directory is pinned to
LocalApplicationData/dotnetcorethroughCustomInstallDir. FAKE 6'sDotNet.installdefaults to~/.dotnetand can also settle on adotnetfound throughPATH; the CI installs the SDK and the .NET 8 runtime the integration tests need into the FAKE 4 location.Fixes found along the way
Targets that were silently doing the wrong thing, each in its own commit:
QuickTestbuilt the test assembly without the version property, soLoading assembly metadata workscompared1.0.0againstRELEASE_NOTES.mdand failed. Only visible locally — CI runsBuildPackage.Skip*variables now count as unset, as they did under FAKE 4.PublishNuGetresolves the mergedpaket.exeabsolutely, sinceToolType.CreateFullFramework()only prefixes Mono for.exepaths.Verification
dotnet fsi build.fsx <Target>type-checks and runs; the generateddotnet build/test/pack/publish/restore/execcommand lines were compared against the FAKE 4 originals, including--filter,--logger,--no-build,--outputand thePackageReleaseNotesFilequoting.Not in scope
The remaining steps of #4348 — replacing the doc generator, dropping the last Mono uses, and moving further onto standard commands — are left for follow-ups.