mirror of
https://github.com/Smaug123/WoofWare.Expect
synced 2025-10-07 21:48:38 +00:00
Atomic file writes (#12)
This commit is contained in:
22
.envrc
22
.envrc
@@ -1 +1,23 @@
|
|||||||
use flake
|
use flake
|
||||||
|
DOTNET_PATH=$(readlink "$(which dotnet)")
|
||||||
|
SETTINGS_FILE=$(find . -maxdepth 1 -type f -name '*.sln.DotSettings.user')
|
||||||
|
MSBUILD=$(realpath "$(find "$(dirname "$DOTNET_PATH")/../share/dotnet/sdk" -maxdepth 2 -type f -name MSBuild.dll)")
|
||||||
|
if [ -f "$SETTINGS_FILE" ] ; then
|
||||||
|
xmlstarlet ed --inplace \
|
||||||
|
-N wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" \
|
||||||
|
-N x="http://schemas.microsoft.com/winfx/2006/xaml" \
|
||||||
|
-N s="clr-namespace:System;assembly=mscorlib" \
|
||||||
|
-N ss="urn:shemas-jetbrains-com:settings-storage-xaml" \
|
||||||
|
--update "//s:String[@x:Key='/Default/Environment/Hierarchy/Build/BuildTool/DotNetCliExePath/@EntryValue']" \
|
||||||
|
--value "$(realpath "$(dirname "$DOTNET_PATH")/../share/dotnet/dotnet")" \
|
||||||
|
"$SETTINGS_FILE"
|
||||||
|
|
||||||
|
xmlstarlet ed --inplace \
|
||||||
|
-N wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation" \
|
||||||
|
-N x="http://schemas.microsoft.com/winfx/2006/xaml" \
|
||||||
|
-N s="clr-namespace:System;assembly=mscorlib" \
|
||||||
|
-N ss="urn:shemas-jetbrains-com:settings-storage-xaml" \
|
||||||
|
--update "//s:String[@x:Key='/Default/Environment/Hierarchy/Build/BuildTool/CustomBuildToolPath/@EntryValue']" \
|
||||||
|
--value "$MSBUILD" \
|
||||||
|
"$SETTINGS_FILE"
|
||||||
|
fi
|
||||||
|
@@ -270,7 +270,7 @@ type ExpectBuilder (mode : Mode) =
|
|||||||
let lines = File.ReadAllLines state.Caller.FilePath
|
let lines = File.ReadAllLines state.Caller.FilePath
|
||||||
let oldContents = String.concat "\n" lines
|
let oldContents = String.concat "\n" lines
|
||||||
let lines = SnapshotUpdate.updateSnapshotAtLine lines state.Caller.LineNumber actual
|
let lines = SnapshotUpdate.updateSnapshotAtLine lines state.Caller.LineNumber actual
|
||||||
File.WriteAllLines (state.Caller.FilePath, lines)
|
File.writeAllLines lines state.Caller.FilePath
|
||||||
failwith ("Snapshot successfully updated. Previous contents:\n" + oldContents)
|
failwith ("Snapshot successfully updated. Previous contents:\n" + oldContents)
|
||||||
|
|
||||||
match CompletedSnapshotGeneric.passesAssertion state with
|
match CompletedSnapshotGeneric.passesAssertion state with
|
||||||
|
32
WoofWare.Expect/File.fs
Normal file
32
WoofWare.Expect/File.fs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
namespace WoofWare.Expect
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.IO
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal File =
|
||||||
|
|
||||||
|
/// Standard attempt at an atomic file write.
|
||||||
|
/// It may fail to be atomic if the working directory somehow spans multiple volumes,
|
||||||
|
/// and of course with external network storage all bets are off.
|
||||||
|
let writeAllLines (lines : string[]) (path : string) : unit =
|
||||||
|
let file = FileInfo path
|
||||||
|
|
||||||
|
let tempFile =
|
||||||
|
Path.Combine (file.Directory.FullName, file.Name + "." + Guid.NewGuid().ToString () + ".tmp")
|
||||||
|
|
||||||
|
try
|
||||||
|
File.WriteAllLines (tempFile, lines)
|
||||||
|
// Atomicity guarantees are undocumented, but on Unix this is an atomic `rename` call
|
||||||
|
// https://github.com/dotnet/runtime/blob/9a4be5b56d81aa04c7ea687c02b3f4e64c83761b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Unix.cs#L181
|
||||||
|
// and on Windows this is an atomic ReplaceFile:
|
||||||
|
// https://github.com/dotnet/runtime/blob/9a4be5b56d81aa04c7ea687c02b3f4e64c83761b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs#L92
|
||||||
|
// calls https://github.com/dotnet/runtime/blob/9a4be5b56d81aa04c7ea687c02b3f4e64c83761b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.ReplaceFile.cs#L12
|
||||||
|
// which calls ReplaceFileW, whose atomicity guarantees are again apparently undocumented,
|
||||||
|
// but 4o-turbo, Opus 4, and Gemini 2.5 Flash all think it's atomic.
|
||||||
|
File.Replace (tempFile, path, null)
|
||||||
|
finally
|
||||||
|
try
|
||||||
|
File.Delete tempFile
|
||||||
|
with _ ->
|
||||||
|
()
|
@@ -300,5 +300,5 @@ module internal SnapshotUpdate =
|
|||||||
|
|
||||||
let newContents = updateAllLines contents sources
|
let newContents = updateAllLines contents sources
|
||||||
|
|
||||||
System.IO.File.WriteAllLines (callerFile, newContents)
|
File.writeAllLines newContents callerFile
|
||||||
)
|
)
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="AssemblyInfo.fs" />
|
<Compile Include="AssemblyInfo.fs" />
|
||||||
<Compile Include="Text.fs" />
|
<Compile Include="Text.fs" />
|
||||||
|
<Compile Include="File.fs" />
|
||||||
<Compile Include="Domain.fs" />
|
<Compile Include="Domain.fs" />
|
||||||
<Compile Include="SnapshotUpdate.fs" />
|
<Compile Include="SnapshotUpdate.fs" />
|
||||||
<Compile Include="Config.fs" />
|
<Compile Include="Config.fs" />
|
||||||
|
Reference in New Issue
Block a user