ScriptContext provides automatic working directory management for .NET 10 file-based apps (single-file C# scripts). It ensures your script's working directory is properly set and restored, even when using Environment.Exit().
- .NET 10 SDK
- TimeWarp.Amuru package
#!/usr/bin/dotnet --
#:package TimeWarp.Amuru
using TimeWarp.Amuru;
// Automatically change to script's directory and restore on disposal
using (var context = ScriptContext.FromEntryPoint())
{
Console.WriteLine($"Script: {context.ScriptFilePath}");
Console.WriteLine($"Working in: {context.ScriptDirectory}");
// Your script operations here
// Working directory is the script's directory
var files = await Shell.Builder("ls").GetLinesAsync();
}
// Original directory automatically restoredWhen your script needs to work from a different directory relative to the script's location:
using (var context = ScriptContext.FromRelativePath(".."))
{
// Now working from parent directory
await Shell.Builder("dotnet", "build").ExecuteAsync();
}
// Original directory restoredAdd cleanup code that runs even if the script exits abruptly:
using (var context = ScriptContext.FromEntryPoint(
changeToScriptDirectory: true,
onExit: () =>
{
Console.WriteLine("Cleaning up temporary files...");
File.Delete("temp.txt");
}))
{
// Your script work
if (error)
Environment.Exit(1); // Cleanup still runs!
}Sometimes you want the context without changing directories:
using (var context = ScriptContext.FromEntryPoint(changeToScriptDirectory: false))
{
// Access script path without changing directory
Console.WriteLine($"Script at: {context.ScriptFilePath}");
Console.WriteLine($"Still in: {Directory.GetCurrentDirectory()}");
}- On Creation: Captures current directory, optionally changes to script or relative directory
- During Use: Provides access to script metadata through properties
- On Disposal: Restores original directory and runs cleanup callbacks
- On Process Exit: Intercepts
Environment.Exit()and unhandled exceptions to ensure cleanup
- Always use
usingblocks to ensure proper disposal - Place ScriptContext at the start of your script's Main method
- Use relative paths after changing directory
- Add cleanup callbacks for critical resources
- Prefer ScriptContext over manual directory management
using (var context = ScriptContext.FromRelativePath(".."))
{
await DotNet.Build()
.WithProject("./Source/Project.csproj")
.ExecuteAsync();
}using (var context = ScriptContext.FromEntryPoint())
{
var testFiles = await Shell.Builder("find")
.WithArguments(".", "-name", "*.Test.cs")
.GetLinesAsync();
foreach (var test in testFiles)
{
await Shell.Builder(test).ExecuteAsync();
}
}- Ensure you're running as a .NET 10 file-based app (not
dotnet runon a project) - Use the shebang:
#!/usr/bin/dotnet --
- Check that you're using a
usingblock or callingDispose() - The directory is restored even with
Environment.Exit(), but not on process kill
- Cleanup runs on normal disposal,
Environment.Exit(), and unhandled exceptions - It does NOT run on
kill -9or Windows Task Manager force-quit