feat: moved root directory into the IO project

This commit is contained in:
D. Moonfire 2023-08-02 08:28:01 -05:00
parent f32eca146e
commit 75c3316709
26 changed files with 380 additions and 138 deletions

View file

@ -6,6 +6,7 @@ using Autofac;
using MfGames.IO.Extensions;
using MfGames.Nitride;
using MfGames.Nitride.IO;
using MfGames.Nitride.IO.Setup;
namespace CopyFiles;
@ -14,37 +15,36 @@ namespace CopyFiles;
/// </summary>
public static class CopyFilesProgram
{
public static async Task<int> Main(string[] args)
{
// All of the builder methods are fluent in that they return the
// builder to allow them to be chained. However, for documentation
// purposes, we are going to split them apart to explain the details.
var builder = new NitrideBuilder(args);
public static async Task<int> Main(string[] args)
{
// All of the builder methods are fluent in that they return the
// builder to allow them to be chained. However, for documentation
// purposes, we are going to split them apart to explain the details.
var builder = new NitrideBuilder(args);
// Filesystem access is provided by Zio, which allows for mutliple
// folders to be combined together into a single unified file
// system. At the moment, we set the "root" directory which will
// contains all the paths, both input and output.
DirectoryInfo rootDir = typeof(CopyFilesProgram)
.GetDirectory()!
.FindGitRoot()!
.GetDirectory("examples/NitrideCopyFiles");
// Filesystem access is provided by Zio, which allows for mutliple
// folders to be combined together into a single unified file
// system. At the moment, we set the "root" directory which will
// contains all the paths, both input and output.
//
// Like Serilog, we use a number of extension methods on the builder
// to inject functionality such as plugins and extensions. In this
// case, we only need the Nitride.IO module so we use the `UseIO`
// to inject the requisite modules and configure it.
DirectoryInfo rootDir = typeof(CopyFilesProgram)
.GetDirectory()!
.FindGitRoot()!
.GetDirectory("examples/NitrideCopyFiles");
builder.WithRootDirectory(rootDir);
builder.UseIO(rootDir);
// Like Serilog, we use a number of extension methods on the builder
// to inject functionality such as plugins and extensions. In this
// case, we only need the Nitride.IO module so we use the `UseIO`
// to inject the requisite modules and configure it.
builder.UseIO();
// We use Autofac for the bulk of our registration handling. This
// was mainly because we are more comfortable with Autofac, but it
// also has a clean interface for handling some of the more esoteric
// problems we've encountered.
builder.ConfigureContainer(x => x.RegisterModule<CopyFilesModule>());
// We use Autofac for the bulk of our registration handling. This
// was mainly because we are more comfortable with Autofac, but it
// also has a clean interface for handling some of the more esoteric
// problems we've encountered.
builder.ConfigureContainer(x => x.RegisterModule<CopyFilesModule>());
// Finally, we build the site generator object and run it.
return await builder.RunAsync();
}
// Finally, we build the site generator object and run it.
return await builder.RunAsync();
}
}

View file

@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MfGames.Gallium;
using MfGames.Nitride.Pipelines;
@ -32,7 +33,8 @@ public class DelayPipeline1 : PipelineBase
.Select(
entity =>
{
Thread.Sleep(1000);
Task.Delay(1000, cancellationToken).Wait(cancellationToken);
this.logger.Information(
"Delayed {Value}",
entity.Get<UPath>());

View file

@ -6,6 +6,7 @@ using Autofac;
using MfGames.IO.Extensions;
using MfGames.Nitride;
using MfGames.Nitride.IO;
using MfGames.Nitride.IO.Setup;
namespace NitridePipelines;
@ -22,8 +23,7 @@ public static class NitridePipelinesProgram
.GetDirectory("examples/NitridePipelines");
return await new NitrideBuilder(args)
.UseIO()
.WithRootDirectory(rootDir)
.UseIO(rootDir)
.ConfigureContainer(
x => x.RegisterModule<NitridePipelinesModule>())
.RunAsync();

View file

@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MfGames.Gallium;
using MfGames.Nitride.Pipelines;
@ -33,7 +34,7 @@ public class OutputPipeline1 : PipelineBase
.Select(
entity =>
{
Thread.Sleep(1000);
Task.Delay(1000, cancellationToken).Wait(cancellationToken);
this.logger.Information(
"Pretended to write {Value}",

View file

@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MfGames.Gallium;
using MfGames.Nitride.Pipelines;
@ -32,7 +33,7 @@ public class OutputPipeline2 : PipelineBase
.Select(
entity =>
{
Thread.Sleep(1000);
Task.Delay(1000, cancellationToken).Wait(cancellationToken);
this.logger.Information(
"Pretended to write {Value}",

View file

@ -16,7 +16,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MfGames.Nitride\MfGames.Nitride.csproj"/>
<ProjectReference Include="..\MfGames.Nitride\MfGames.Nitride.csproj" />
</ItemGroup>
<!-- Include the source generator -->
@ -38,4 +38,10 @@
</PackageReference>
</ItemGroup>
<ItemGroup>
<Reference Include="Zio">
<HintPath>..\..\..\..\..\.nuget\packages\zio\0.16.2\lib\net6.0\Zio.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

View file

@ -4,7 +4,7 @@ using MfGames.Gallium;
using Zio;
namespace MfGames.Nitride.IO;
namespace MfGames.Nitride.IO.Extensions;
/// <summary>
/// Extension methods for working with paths.

View file

@ -16,20 +16,20 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="7.0.1"/>
<PackageReference Include="DotNet.Glob" Version="3.1.3"/>
<PackageReference Include="FluentValidation" Version="11.6.0"/>
<PackageReference Include="Autofac" Version="7.0.1" />
<PackageReference Include="DotNet.Glob" Version="3.1.3" />
<PackageReference Include="FluentValidation" Version="11.6.0" />
<PackageReference Include="GitVersion.MSBuild" Version="5.12.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="MAB.DotIgnore" Version="3.0.2"/>
<PackageReference Include="Serilog" Version="3.0.1"/>
<PackageReference Include="Zio" Version="0.16.2"/>
<PackageReference Include="MAB.DotIgnore" Version="3.0.2" />
<PackageReference Include="Serilog" Version="3.0.1" />
<PackageReference Include="Zio" Version="0.16.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MfGames.Nitride\MfGames.Nitride.csproj"/>
<ProjectReference Include="..\MfGames.Nitride\MfGames.Nitride.csproj" />
</ItemGroup>
<!-- Include the source generator -->

View file

@ -1,12 +0,0 @@
using Autofac;
namespace MfGames.Nitride.IO;
public static class NitrideIOBuilderExtensions
{
public static NitrideBuilder UseIO(this NitrideBuilder builder)
{
return builder.ConfigureContainer(
x => x.RegisterModule<NitrideIOModule>());
}
}

View file

@ -1,13 +0,0 @@
using Autofac;
namespace MfGames.Nitride.IO;
public class NitrideIOModule : Module
{
/// <inheritdoc />
protected override void Load(ContainerBuilder builder)
{
builder.RegisterOperators(this);
builder.RegisterValidators(this);
}
}

View file

@ -0,0 +1,21 @@
namespace MfGames.Nitride.IO.Setup;
public enum DefaultRootDirectoryHandling
{
/// <summary>
/// Indicates that the Git root, if one is found, should be used as the
/// default Git root.
/// </summary>
GitRoot,
/// <summary>
/// Indicates than an exception should be shown if a directory is not set.
/// </summary>
ThrowError,
/// <summary>
/// Indicates that the current working directory should be used for the
/// default working directory.
/// </summary>
WorkingDirectory,
}

View file

@ -0,0 +1,133 @@
using MfGames.IO.Extensions;
using MfGames.Nitride.Generators;
using Serilog;
using Zio;
using Zio.FileSystems;
namespace MfGames.Nitride.IO.Setup;
/// <summary>
/// A service for managing and keeping track of the directories used for the
/// build process.
/// </summary>
[WithProperties]
public partial class DirectoryService
{
private readonly ILogger logger;
private IFileSystem? fileSystem;
private DirectoryInfo? rootDirectory;
public DirectoryService(
ILogger logger,
NitrideIOConfiguration config)
{
this.logger = logger.ForContext<DirectoryService>();
this.rootDirectory ??= config.RootDirectory;
this.fileSystem ??= config.FileSystem;
}
/// <summary>
/// Gets or sets the handling when a root directory is not provided.
/// This defaults to using the Git root as that is the most likely use for
/// most Nitride installations.
/// </summary>
public DefaultRootDirectoryHandling DefaultRootDirectoryHandling
{
get;
set;
}
/// <summary>
/// Gets or sets the root directory
/// </summary>
public DirectoryInfo? RootDirectory
{
get => this.rootDirectory;
set
{
this.rootDirectory = value;
this.fileSystem = null;
}
}
/// <summary>
/// Constructs the file system from the given root directory. If this is
/// not configured, then a root directory is given based on the working
/// directory or the Git root.
/// </summary>
public IFileSystem GetOrCreateFileSystem()
{
// If we already have a file system, then just return it.
if (this.fileSystem != null)
{
return this.fileSystem;
}
// If we have a root directory, then use that. Otherwise, we need to
// calculate it (and report to the user).
DirectoryInfo? directory = this.RootDirectory;
if (directory == null)
{
switch (this.DefaultRootDirectoryHandling)
{
case DefaultRootDirectoryHandling.GitRoot:
directory =
new DirectoryInfo(Environment.CurrentDirectory)
.FindGitRoot();
if (directory == null)
{
throw new InvalidOperationException(
"Cannot create an IFileSystem without the RootDirectory"
+ " being set on DirectoryService and a Git root directory"
+ " unable to be found start at "
+ Environment.CurrentDirectory
+ ".");
}
this.logger.Verbose(
"Setting root directory to Git root directory: {Path}",
directory.FullName);
break;
case DefaultRootDirectoryHandling.ThrowError:
throw new InvalidOperationException(
"Cannot create an IFileSystem without the RootDirectory"
+ " being set on DirectoryService or the DefaultRootDirectoryHandling"
+ " property set to a value other than ThrowsError.");
case DefaultRootDirectoryHandling.WorkingDirectory:
directory =
new DirectoryInfo(Environment.CurrentDirectory);
this.logger.Verbose(
"Setting root directory to current working directory: {Path}",
directory.FullName);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
// At this point, the root directory is set to something, so use it.
this.logger.Debug(
"Setting root directory and filesystem to {Path}",
this.RootDirectory!.FullName);
var rootFileSystem = new PhysicalFileSystem();
this.fileSystem = new SubFileSystem(
rootFileSystem,
this.RootDirectory.FullName);
return this.fileSystem;
}
}

View file

@ -0,0 +1,50 @@
using Autofac;
using MfGames.Nitride.Commands;
using Zio;
namespace MfGames.Nitride.IO.Setup;
/// <summary>
/// Extensions methods for NitrideBuilder for using the IO packages.
/// </summary>
public static class NitrideIOBuilderExtensions
{
public static NitrideBuilder UseIO(
this NitrideBuilder builder,
DirectoryInfo rootDirectory)
{
return UseIO(
builder,
x => x.RootDirectory = rootDirectory);
}
public static NitrideBuilder UseIO(
this NitrideBuilder builder,
Action<NitrideIOConfiguration>? configure = null)
{
// Get the configuration so we can set the various options.
var config = new NitrideIOConfiguration();
configure?.Invoke(config);
builder.ConfigureContainer(
x =>
{
// Register our module and configuration.
x.RegisterModule<NitrideIOModule>();
x.RegisterInstance(config).AsSelf().SingleInstance();
// Add in the CLI options.
if (config.AddDirectoryCommandLineOption)
{
x
.RegisterType<RootDirectoryCommandLineOption>()
.As<IPipelineCommandOption>();
}
});
return builder;
}
}

View file

@ -0,0 +1,29 @@
using MfGames.Nitride.Generators;
using Zio;
namespace MfGames.Nitride.IO.Setup;
/// <summary>
/// Configuration class for setting up Nitride.
/// </summary>
[WithProperties]
public partial class NitrideIOConfiguration
{
/// <summary>
/// Gets or sets a value indicating whether the `--directory` command line
/// option should be added to the build and watch commands.
/// </summary>
public bool AddDirectoryCommandLineOption { get; set; }
/// <summary>
/// Gets or sets the file system to use with the process. This is used to
/// override the filesystem entirely.
/// </summary>
public IFileSystem? FileSystem { get; set; }
/// <summary>
/// Gets or sets the root directory to use.
/// </summary>
public DirectoryInfo? RootDirectory { get; set; }
}

View file

@ -0,0 +1,29 @@
using Autofac;
using Zio;
namespace MfGames.Nitride.IO.Setup;
public class NitrideIOModule : Module
{
/// <inheritdoc />
protected override void Load(ContainerBuilder builder)
{
// Register the directory service which is used as the source of truth
// for the file system.
builder.RegisterType<DirectoryService>().AsSelf().SingleInstance();
// Register the IFileSystem as the one inside the directory service,
// which lets us change it from the command line as needed.
builder
.Register(
(context) => context
.Resolve<DirectoryService>()
.GetOrCreateFileSystem())
.As<IFileSystem>();
// Add in the operators and validators.
builder.RegisterOperators(this);
builder.RegisterValidators(this);
}
}

View file

@ -0,0 +1,47 @@
using System.CommandLine;
using System.CommandLine.Invocation;
using MfGames.Nitride.Commands;
using Serilog;
namespace MfGames.Nitride.IO.Setup;
/// <summary>
/// A factory to inject the "--directory=XXX" argument into the build
/// and other pipeline commands.
/// </summary>
public class RootDirectoryCommandLineOption : IPipelineCommandOption
{
private readonly ILogger logger;
private readonly DirectoryService service;
public RootDirectoryCommandLineOption(
ILogger logger,
DirectoryService service)
{
this.service = service;
this.logger = logger.ForContext<RootDirectoryCommandLineOption>();
this.Option = new Option<DirectoryInfo?>("--directory")
{
Description = "Sets the root directory",
ArgumentHelpName = "DIR",
};
}
/// <inheritdoc />
public Option Option { get; }
/// <inheritdoc />
public void Handle(InvocationContext context)
{
this.service.RootDirectory = (DirectoryInfo?)context.ParseResult
.GetValueForOption(this.Option);
this.logger.Information(
"Setting root directory from arguments: {Path}",
this.service.RootDirectory);
}
}

View file

@ -16,21 +16,21 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="7.0.1"/>
<PackageReference Include="Autofac" Version="7.0.1" />
<PackageReference Include="GitVersion.MSBuild" Version="5.12.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0"/>
<PackageReference Include="NodaTime" Version="3.1.9"/>
<PackageReference Include="NodaTime.Testing" Version="3.1.9"/>
<PackageReference Include="Serilog" Version="3.0.1"/>
<PackageReference Include="TimeSpanParserUtil" Version="1.2.0"/>
<PackageReference Include="Zio" Version="0.16.2"/>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
<PackageReference Include="NodaTime" Version="3.1.9" />
<PackageReference Include="NodaTime.Testing" Version="3.1.9" />
<PackageReference Include="Serilog" Version="3.0.1" />
<PackageReference Include="TimeSpanParserUtil" Version="1.2.0" />
<PackageReference Include="Zio" Version="0.16.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MfGames.Nitride\MfGames.Nitride.csproj"/>
<ProjectReference Include="..\MfGames.Nitride\MfGames.Nitride.csproj" />
</ItemGroup>
<!-- Include the source generator -->

View file

@ -9,7 +9,7 @@ using NodaTime.Testing;
using Serilog;
namespace MfGames.Nitride.Temporal.Cli;
namespace MfGames.Nitride.Temporal.Setup;
/// <summary>
/// A factory to inject the "--date=XXXX-XX-XX" argument into the build

View file

@ -10,7 +10,7 @@ using Serilog;
using TimeSpanParserUtil;
namespace MfGames.Nitride.Temporal.Cli;
namespace MfGames.Nitride.Temporal.Setup;
/// <summary>
/// A factory to inject the "--expires=XXXX" argument into the build

View file

@ -1,7 +1,6 @@
using Autofac;
using MfGames.Nitride.Commands;
using MfGames.Nitride.Temporal.Cli;
using Serilog;

View file

@ -10,6 +10,11 @@ namespace MfGames.Nitride.Temporal.Setup;
[WithProperties]
public partial class NitrideTemporalConfiguration
{
public NitrideTemporalConfiguration()
{
this.AddDateOptionToCommandLine = true;
}
/// <summary>
/// Adds the "--date=XXXX-XX-XX" option into the pipeline commands.
/// </summary>

View file

@ -38,7 +38,6 @@
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="Zio" Version="0.16.2" />
</ItemGroup>
<!-- Include the source generator -->

View file

@ -4,11 +4,6 @@ using Autofac;
using MfGames.ToolBuilder;
using Serilog;
using Zio;
using Zio.FileSystems;
using Module = Autofac.Module;
namespace MfGames.Nitride;
@ -46,13 +41,6 @@ public class NitrideBuilder
.Name!;
}
/// <summary>
/// Gets or sets the input directory to automatically register as the
/// source of the files. If this is not set, then no Zio.IFileSystem
/// will be registered and it will have to be done manually.
/// </summary>
public DirectoryInfo? RootDirectory { get; set; }
/// <summary>
/// Allows for configuration of the Autofac container to register
/// additional types, pipelines, and modules.
@ -145,32 +133,11 @@ public class NitrideBuilder
return this;
}
/// <summary>
/// Sets the root directory to a common value by creating a
/// IFileSystem (from Zio) of the root directory and registering it.
/// This will be used for both input and output.
/// </summary>
/// <param name="directory">
/// The path to the directory that represents "/" while
/// building.
/// </param>
/// <returns>The builder to chain calls.</returns>
public NitrideBuilder WithRootDirectory(DirectoryInfo directory)
{
this.RootDirectory = directory;
return this;
}
private void ConfigureContainer(ContainerBuilder builder)
{
// Hook up the rest of the modules.
builder.RegisterModule(this.nitrideModule);
// Set up our file system.
// TODO Wrong logger
this.RegisterRootDirectory(Log.Logger, builder);
// Finish up the registration by running our events.
foreach (Action<NitrideBuilder, ILifetimeScope>? callback in this
.configureSiteCallbacks)
@ -184,29 +151,4 @@ public class NitrideBuilder
configureContainer.Invoke(builder);
}
}
private void RegisterRootDirectory(
ILogger logger,
ContainerBuilder builder)
{
if (this.RootDirectory == null)
{
logger.Verbose("No root directory is registered");
return;
}
logger.Debug(
"Setting root directory to {Path}",
this.RootDirectory.FullName);
var rootFileSystem = new PhysicalFileSystem();
var subFileSystem = new SubFileSystem(
rootFileSystem,
this.RootDirectory.FullName);
builder.RegisterInstance(subFileSystem)
.As<IFileSystem>()
.SingleInstance();
}
}

View file

@ -1,5 +1,6 @@
using Autofac;
using MfGames.Nitride.IO.Setup;
using MfGames.Nitride.Tests;
using Zio;

View file

@ -3,6 +3,7 @@ using System.Linq;
using MfGames.Gallium;
using MfGames.Nitride.IO.Contents;
using MfGames.Nitride.IO.Extensions;
using Xunit;
using Xunit.Abstractions;

View file

@ -5,6 +5,7 @@ using MAB.DotIgnore;
using MfGames.Gallium;
using MfGames.Nitride.IO.Contents;
using MfGames.Nitride.IO.Extensions;
using Xunit;
using Xunit.Abstractions;